From 7c5da0f7f69633cb0776fdf474b86c8f7163a973 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 23 Jul 2026 10:05:44 +0700 Subject: [PATCH] docs(stock): add stock-events plan artifacts --- .../phase-01-extend-ssi-corporate-events.md | 52 +++++++++++++++ .../phase-02-add-telegram-command.md | 59 +++++++++++++++++ .../phase-03-verify-and-document.md | 53 +++++++++++++++ .../260723-0852-stock-events-command/plan.md | 59 +++++++++++++++++ .../harness/adversarial-validation.json | 9 +++ .../reports/harness/context-snippets.json | 41 ++++++++++++ .../reports/harness/review-decision.json | 24 +++++++ .../reports/harness/risk-gate.json | 10 +++ .../reports/harness/verification.json | 64 +++++++++++++++++++ .../pm-260723-1003-stock-events-command.md | 42 ++++++++++++ 10 files changed, 413 insertions(+) create mode 100644 plans/260723-0852-stock-events-command/phase-01-extend-ssi-corporate-events.md create mode 100644 plans/260723-0852-stock-events-command/phase-02-add-telegram-command.md create mode 100644 plans/260723-0852-stock-events-command/phase-03-verify-and-document.md create mode 100644 plans/260723-0852-stock-events-command/plan.md create mode 100644 plans/260723-0852-stock-events-command/reports/harness/adversarial-validation.json create mode 100644 plans/260723-0852-stock-events-command/reports/harness/context-snippets.json create mode 100644 plans/260723-0852-stock-events-command/reports/harness/review-decision.json create mode 100644 plans/260723-0852-stock-events-command/reports/harness/risk-gate.json create mode 100644 plans/260723-0852-stock-events-command/reports/harness/verification.json create mode 100644 plans/260723-0852-stock-events-command/reports/pm-260723-1003-stock-events-command.md diff --git a/plans/260723-0852-stock-events-command/phase-01-extend-ssi-corporate-events.md b/plans/260723-0852-stock-events-command/phase-01-extend-ssi-corporate-events.md new file mode 100644 index 0000000..f73cb24 --- /dev/null +++ b/plans/260723-0852-stock-events-command/phase-01-extend-ssi-corporate-events.md @@ -0,0 +1,52 @@ +--- +phase: 1 +title: Extend SSI Corporate Events +status: completed +priority: P2 +dependencies: [] +--- + +# Phase 1: Extend SSI Corporate Events + +## Overview +Add an additive SSI corporate-action fetch path for read-only event listing. Preserve the current dividend-specific contract used by `/stock_portfolio` and retained dividend history. + +## Requirements +- Functional: fetch all SSI corporate-action rows for one normalized symbol in `(after, through]`, with the same one-day Asia/Saigon overlap and provider page caps already enforced in `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:23` and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:113`. +- Functional: deduplicate by SSI `CorId`, then return deterministic chronological order by `PublishedAt`, tie-broken by provider ID as today’s dividend path does at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:160`. +- Functional: preserve SSI raw display fields for the generic command path: symbol, provider ID, SSI type code/label, title/description/name, published/ex/record/payment strings, value, ratio, and source URL. +- Functional: use a private cursor timestamp only for filtering and sorting; do not replace the raw SSI date strings with normalized event dates in the display model. +- Functional: keep `FetchDividendEvents` behavior unchanged for dividend discovery and `/stock_portfolio` notifications (`C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events.go:12`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_notifications.go:204`). +- Non-functional: no persistence, no callback/button work, no schema/index/system-state changes. + +## Architecture +`SSIDividendProvider` already owns SSI HTTP access, page fetches, date parsing, and source-link generation in `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:92`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:172`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:247`, and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:309`. Phase 1 adds a parallel generic event model plus an additive fetch method that reuses those helpers, but does not widen `DividendEvent` validation. + +Data flow: +1. Symbol and time window enter the generic provider. +2. SSI pages are fetched with the existing timeout/cap logic. +3. Each raw SSI row is copied into a generic display model with minimal validation: matching symbol, valid provider ID, and a private cursor timestamp via `publicDate` or the existing fallback chain. +4. Generic rows are sorted and returned to the command layer with raw SSI strings intact; dividend-only normalization remains on the separate `FetchDividendEvents` path. + +## Related Code Files +- Create: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock_events.go` — introduce the generic stock-event model and provider interface shared by the SSI provider and command handler. +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go` — add the additive generic SSI fetch method and generic row normalizer while keeping dividend normalization intact. +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi_test.go` — cover generic pagination/dedup/range/order behavior and explicit dividend-regression expectations. + +## Implementation Steps +1. Define a generic read-only event type and provider interface separate from `DividendEvent` so `/stock_portfolio` persistence rules are not broadened. +2. Add an additive SSI fetch method that reuses `fetchPage`, `startOfSaigonDay`, `parseSSIOptionalDate`, `parseSSIDate`, and `eventSourceURL` instead of introducing a second transport/client stack. +3. Normalize every matching SSI row into a generic display model with title fallback order `EventTitle -> EventDescription -> EventName -> EventListCode`, while requiring only data needed for chronological display. +4. Keep `FetchDividendEvents` and `normalizeEvent` behavior additive-only: no relaxed dividend validation for value/ratio fields, no changes to portfolio history serialization. +5. Add provider tests for cross-page deduplication, one-day overlap filtering, deterministic ordering, generic inclusion of non-dividend corporate actions, and a regression assertion that dividend discovery still excludes non-dividend rows. + +## Success Criteria +- [x] Generic SSI fetch returns all eligible corporate actions in deterministic chronological order for one ticker and lookback window. +- [x] Generic fetch propagates upstream/paging/decode errors to the caller instead of silently succeeding with partial pages. +- [x] Existing dividend-provider tests still pass with no behavior drift in `/stock_portfolio` discovery. + +## Risk Assessment +- High: weakening dividend validation would change `/stock_portfolio` suggestions and history merges. Mitigation: additive generic method only; keep dividend-specific tests green before Phase 2. +- Medium: malformed SSI rows could create unstable or unsortable output. Mitigation: require a usable publish timestamp and provider ID; skip rows that cannot be placed chronologically. +- Medium: over-refactoring the proven dividend loop increases blast radius. Mitigation: reuse helper functions first; duplicate only the page-scan control flow if that keeps the dividend path unchanged. +- Rollback: revert the generic provider additions and tests only. No stored data or public command contract exists yet, so rollback is code-only and isolated. diff --git a/plans/260723-0852-stock-events-command/phase-02-add-telegram-command.md b/plans/260723-0852-stock-events-command/phase-02-add-telegram-command.md new file mode 100644 index 0000000..f453363 --- /dev/null +++ b/plans/260723-0852-stock-events-command/phase-02-add-telegram-command.md @@ -0,0 +1,59 @@ +--- +phase: 2 +title: "Add Telegram Command" +status: completed +priority: P2 +dependencies: [1] +--- + +# Phase 2: Add Telegram Command + +## Overview +Expose `/stock_events [days]` as a public, read-only Telegram command. Keep it independent from sender-specific portfolio state and produce deterministic, Telegram-safe SSI event output. + +## Requirements +- Functional: register public command metadata in `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock.go:13` with exact parameters ` [days]` and a concise description compatible with `/help` and Telegram’s native menu (`C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\command_presentation.go:6`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\validate.go:14`). +- Functional: accept exactly 1 required arg plus 1 optional days arg; default days to 30; allow only whole numbers `1..90`; reject missing or extra args with exact usage text. +- Functional: normalize ticker with `normalizeStockSymbol` at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\symbols.go:18`; surface the same unknown-ticker wording used by current stock handlers at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers.go:93`. +- Functional: do not require `senderInfo`, `LoadPortfolio`, `PendingDividendStore`, or any write path from `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers.go:59` and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\portfolio.go:64`. +- Functional: respond cleanly for no events, upstream/provider failure, and parse failure. +- Non-functional: preserve provider order, bound every message below the repo’s 4000-char Telegram safety margin used at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers.go:480` and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stats\views.go:17`. + +## Architecture +Data flow: +1. Dispatcher routes the public command to `handleStockEvents`. +2. The handler parses args, defaults/validates `days`, normalizes the ticker, and computes `after = now - days*24h`, `through = now`. +3. The handler calls the generic SSI provider from Phase 1 and receives chronologically sorted corporate actions. +4. Each event becomes a plain-text block with symbol, SSI type, bounded title, populated dates, and `SSI event: ` plus the raw source URL when present. +5. Blocks are packed into `<=4000`-char replies without splitting an event block; multi-message output adds deterministic part numbering. If one block still exceeds budget after field truncation, hard-truncate the final line with `…(truncated)`. + +Formatting strategy: +- Reuse the package-level rune-safe truncation helper from `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_notifications.go:363` for long titles and labels. +- Prefer plain-text replies via `chathelper.Reply` instead of HTML tables so raw SSI URLs stay visible and chunking stays simple. +- Keep chronological ordering exactly as returned by the provider; do not re-sort by type or date label in the handler. + +## Related Code Files +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers.go` — add state access to the generic provider if needed, but keep sender/store logic untouched for this command. +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock.go` — register `/stock_events` metadata and handler. +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock_events.go` — implement args parsing, provider call, event formatting, and reply chunking. +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers_test.go` — add registration, usage, validation, no-sender, no-events, upstream-error, and chunking coverage. + +## Implementation Steps +1. Wire the state to a generic stock-event provider with a zero-write fallback path suitable for tests that do not initialize stores or sender data. +2. Register `stock_events` as `VisibilityPublic` with exact metadata: `Parameters: " [days]"`, description concise enough to stay well under the 256-rune menu limit. +3. Implement the command parser so `/stock_events TCB`, `/stock_events TCB 7`, and only those shapes succeed; anything else returns exact usage or range errors. +4. Implement the read-only handler using `s.now()` and the provider from Phase 1. Do not touch portfolio/dividend history, pending buttons, or locks. +5. Format one event block per SSI row with only required fields. Include date labels only when present so missing `RecordDate` or `PaymentDate` does not generate noisy placeholders. +6. Pack reply blocks into bounded Telegram messages, preserve order across chunks, and keep no-events/failure replies single-message and deterministic. +7. Add handler tests for default 30-day behavior, explicit day range, invalid day values, extra args, senderless execution, provider window propagation, empty results, provider error mapping, and over-budget output chunking. + +## Success Criteria +- [x] `/stock_events [days]` is publicly registered with exact parameters/usage text and no sender requirement. +- [x] The handler produces deterministic chronological output, friendly empty/error replies, and message chunks below the Telegram safety budget. +- [x] The command path is provably read-only: no portfolio loads, pending-action writes, or dividend-history mutations are required for success. + +## Risk Assessment +- High: oversized replies can be rejected by Telegram or split mid-event. Mitigation: chunk by whole event blocks against a 4000-char budget and add an oversized synthetic test. +- Medium: introducing a hidden sender or store dependency would break channel/anonymized use. Mitigation: add a senderless test case and keep the handler isolated from `senderInfo`. +- Medium: metadata drift between handler usage and registration breaks `/help` and the native menu. Mitigation: assert the exact `Parameters` string and usage text in tests. +- Rollback: remove the new command registration and `stock_events` handler changes. Phase 1’s additive provider can remain unused or be reverted separately without data cleanup. diff --git a/plans/260723-0852-stock-events-command/phase-03-verify-and-document.md b/plans/260723-0852-stock-events-command/phase-03-verify-and-document.md new file mode 100644 index 0000000..cce5bf6 --- /dev/null +++ b/plans/260723-0852-stock-events-command/phase-03-verify-and-document.md @@ -0,0 +1,53 @@ +--- +phase: 3 +title: "Verify and Document" +status: completed +priority: P2 +dependencies: [1, 2] +--- + +# Phase 3: Verify and Document + +## Overview +Lock the public contract, prove dividend behavior did not regress, and update user-facing docs. This phase is the quality gate before implementation is considered done. + +## Requirements +- Functional: document `/stock_events [days]`, default 30 days, valid range `1..90`, and read-only SSI behavior in `C:\Users\miti99\Workspaces\tiennm99\miti99bot\README.md:22`. +- Functional: keep existing dividend event behavior unchanged for `/stock_portfolio`, including SSI overlap/refetch behavior already covered in `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_flow_test.go:94`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_flow_test.go:154`, and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_flow_test.go:491`. +- Non-functional: run the repo gates required by `AGENTS.md`: `gofmt`, focused stock tests, `go test ./...`, `go vet ./...`, `go build ./...`, and `golangci-lint run` when the binary is available. + +## Architecture +This phase does not introduce runtime behavior. It validates that: +1. command metadata matches handler usage and README wording; +2. the new generic SSI path did not alter dividend discovery or notification flows; +3. repo-wide compilation, tests, and vet/lint still pass after the additive command lands. + +Test matrix: +- Unit: generic SSI normalization/order tests, days parser, reply chunking. +- Integration: registration metadata, handler no-events/error/senderless behavior, provider window propagation. +- Regression: dividend SSI/provider tests plus `dividend_flow_test.go` scenarios. +- Repository: full `go test ./...`, `go vet ./...`, `go build ./...`, optional `golangci-lint run`. + +## Related Code Files +- Modify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\README.md` — add the new command contract beside the stock command documentation. +- Verify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\handlers_test.go` — command registration/usage/read-only coverage. +- Verify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi_test.go` — generic SSI fetch plus dividend-regression coverage. +- Verify: `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_flow_test.go` — unchanged `/stock_portfolio` dividend notifications. + +## Implementation Steps +1. Update the README stock section with the exact command syntax, default day window, allowed range, and SSI-source caveat so docs match the public command contract. +2. Run focused stock-module tests first to catch fast feedback on provider normalization, handler parsing, chunking, and dividend regressions. +3. Run `gofmt` on touched Go files, then execute `go test ./...`, `go vet ./...`, and `go build ./...`. +4. Run `golangci-lint run` if the binary is available locally; if it is unavailable, record that explicitly rather than silently skipping it. +5. If any gate fails because of the new command/provider work, fix the owning phase before marking this plan complete. + +## Success Criteria +- [x] README, command metadata, and handler usage text all match `/stock_events [days]` exactly. +- [x] Focused stock tests cover the new command and confirm `/stock_portfolio` dividend behavior is unchanged. +- [x] Repo-wide test, vet, and build gates pass; lint status is either passing or explicitly reported as unavailable/pre-existing. + +## Risk Assessment +- Medium: docs can drift from the exact `Parameters` string and create `/help` confusion. Mitigation: compare README text against the registered `Parameters` and handler usage string before closing. +- Medium: additive SSI changes can still regress dividend behavior indirectly. Mitigation: rerun the provider/dividend-flow suites, not just the new command tests. +- Low: repo-wide gates may surface unrelated pre-existing failures. Mitigation: separate new failures from baseline issues and avoid hiding them behind the feature summary. +- Rollback: revert the README and command changes if validation reveals an unacceptable regression. No data migration or cleanup step is needed. diff --git a/plans/260723-0852-stock-events-command/plan.md b/plans/260723-0852-stock-events-command/plan.md new file mode 100644 index 0000000..0b9cd1b --- /dev/null +++ b/plans/260723-0852-stock-events-command/plan.md @@ -0,0 +1,59 @@ +--- +title: Stock Events Command +description: >- + Add a public /stock_events command for read-only SSI corporate action lookup + with bounded Telegram output. +status: completed +priority: P2 +effort: 8h +branch: main +tags: + - feature + - backend + - telegram + - stock +blockedBy: [] +blocks: [] +created: 2026-07-23T00:00:00.000Z +--- + +# Stock Events Command + +## Overview + +Add public `/stock_events [days]` for read-only SSI corporate-action lookup. Reuse SSI pagination helpers from `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events_ssi.go:113` without changing the dividend-history path used by `/stock_portfolio` (`C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_notifications.go:56`). + +## Scope Challenge + +- Existing code: command metadata/help comes from `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\stock.go:13`, `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\command_presentation.go:6`, and `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\validate.go:14`; ticker normalization already exists at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\symbols.go:18`. +- Minimum change: add one parallel read-only SSI event path plus one public command; no portfolio writes, no dividend buttons, no stats/storage migration. +- Complexity: 7 touched files, 1 new command, 0 schema changes, 3 sequential phases. + +## Phases + +| Phase | Name | Status | +|-------|------|--------| +| 1 | [Extend SSI Corporate Events](./phase-01-extend-ssi-corporate-events.md) | Completed | +| 2 | [Add Telegram Command](./phase-02-add-telegram-command.md) | Completed | +| 3 | [Verify and Document](./phase-03-verify-and-document.md) | Completed | + +## Dependencies + +- Cross-plan: None. `plans\260722-1114-compact-portfolio-numbers\plan.md`, `plans\260722-1356-per-user-dividend-history\plan.md`, `plans\260722-1705-mobile-portfolio-columns\plan.md`, and `plans\260722-1843-retire-stock-dividend-command\plan.md` are already completed. +- Phase 2 depends on Phase 1's additive SSI corporate-action fetch contract. +- Phase 3 depends on Phases 1-2 for command behavior, regression tests, and docs alignment. + +## Compatibility + +- Keep dividend-only contracts unchanged: `DividendEventProvider` stays specific to `/stock_portfolio` at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\dividend_events.go:12`, retained history stays under `Portfolio.Dividends` at `C:\Users\miti99\Workspaces\tiennm99\miti99bot\internal\modules\stock\portfolio.go:44`. +- No Mongo/index/system-state/stats work. Out-of-scope items remain untouched. + +## Validation + +- Unit: SSI generic normalization, overlap/date filtering, deterministic order, days parsing, and reply chunking/truncation. +- Integration: command registration metadata, usage/error replies, senderless read-only execution, no-events path, upstream failure path, and dividend regressions. +- Repo gate: `gofmt`, focused stock tests, `go test ./...`, `go vet ./...`, `go build ./...`, and `golangci-lint run` when available. + +## Open Questions + +None. diff --git a/plans/260723-0852-stock-events-command/reports/harness/adversarial-validation.json b/plans/260723-0852-stock-events-command/reports/harness/adversarial-validation.json new file mode 100644 index 0000000..3af6324 --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/harness/adversarial-validation.json @@ -0,0 +1,9 @@ +{ + "decision": "PASS", + "disprovenClaims": [], + "unverifiedClaims": [ + "The implementation cannot prove that every production SSI corporate-action variant remains representable because the endpoint is undocumented. The raw DTO path copies every decoded eventListCode and optional display field without a dividend-type filter (internal/modules/stock/dividend_events_ssi.go:147-180,227-263), while README.md:42-47 correctly limits the promise to eligible, best-effort SSI rows. Fixtures exercise AGM, ISS, OTHER, FALLBACK, and DIV shapes, including malformed optional dates and raw decimal fields (internal/modules/stock/dividend_events_ssi_test.go:82-144), but they are not an authoritative enumeration of SSI's live schema." + ], + "missingProof": [], + "reachableRegressions": [] +} diff --git a/plans/260723-0852-stock-events-command/reports/harness/context-snippets.json b/plans/260723-0852-stock-events-command/reports/harness/context-snippets.json new file mode 100644 index 0000000..bfb2240 --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/harness/context-snippets.json @@ -0,0 +1,41 @@ +{ + "skill": "ck:cook", + "mode": "interactive", + "task": "Add /stock_events [days] for read-only SSI corporate-action lookup", + "acceptanceCriteria": [ + "default lookback is 30 days and an explicit whole-number lookback from 1 through 90 is accepted", + "all eligible SSI corporate actions for the ticker are returned for the exact (after, through] interval", + "raw SSI event name, title, description, date strings, value, and ratio are preserved for display without dividend normalization", + "a malformed optional date remains displayable, a malformed non-empty publicDate is rejected, and a missing publicDate uses the first parseable optional date only as its filter/order cursor", + "SSI pagination is complete, CorId values are deduplicated, and output order is deterministic by publication time then provider ID", + "the command is senderless and read-only, with friendly usage, empty-result, and upstream-error replies", + "every Telegram reply stays below the 4000-character safety budget without reordering events", + "existing dividend normalization, portfolio discovery, retained history, and callback behavior remain unchanged", + "README, command metadata, usage text, registration tests, help/menu discovery, and server expectations remain exact" + ], + "touchpoints": [ + "internal/modules/stock/dividend_events_ssi.go", + "internal/modules/stock/stock_events.go", + "internal/modules/stock/handlers.go", + "internal/modules/stock/stock.go", + "internal/modules/stock/dividend_events_ssi_test.go", + "internal/modules/stock/stock_events_test.go", + "internal/modules/stock/handlers_test.go", + "cmd/server/command_menu_test.go", + "README.md" + ], + "publicContracts": [ + "Telegram command: /stock_events [days]", + "default days: 30; accepted explicit range: 1..90", + "SSIStockEventProvider.FetchStockEvents(ctx, symbol, after, through)", + "DividendEventProvider.FetchDividendEvents(ctx, symbol, after, through) remains unchanged" + ], + "blastRadius": [ + "stock command registration and dispatch", + "Telegram native command menu and /help presentation", + "shared SSI corporate-action HTTP pagination used by stock-event and dividend discovery", + "stock portfolio dividend notification discovery", + "user-facing README contract" + ], + "scoutSummary": "The re-review traced raw SSI fields through pagination, copyStockEvent, cursor selection, formatting, chunking, module registration, help/menu metadata, server discovery, and the unchanged strict dividend caller. Generic events retain malformed optional date strings for display. Non-empty publicDate must parse and cannot fall back; when publicDate is absent, the first parseable optional date is used only for exact interval filtering and deterministic ordering. The command remains senderless, store-free, and concurrency-safe, and no reachable dividend or portfolio regression was found." +} diff --git a/plans/260723-0852-stock-events-command/reports/harness/review-decision.json b/plans/260723-0852-stock-events-command/reports/harness/review-decision.json new file mode 100644 index 0000000..6ef8a3d --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/harness/review-decision.json @@ -0,0 +1,24 @@ +{ + "decision": "PASS", + "score": 9.6, + "criticalCount": 0, + "acceptanceCoverage": [ + "default 30-day and explicit 1..90 parsing are covered by stock_events_test.go", + "exact (after, through] filtering, pagination, CorId deduplication, and deterministic ordering are covered by dividend_events_ssi_test.go", + "senderless execution, empty results, upstream failures, registration metadata, and bounded reply chunking are covered by stock_events_test.go", + "native menu metadata is covered by cmd/server/command_menu_test.go", + "README and handler usage match /stock_events [days]", + "raw name, title, description, dates, value, and ratio are copied and rendered without dividend normalization", + "provider fixtures prove malformed optional dates remain raw/displayable, malformed non-empty publicDate is rejected, and missing publicDate uses a parseable optional cursor" + ], + "regressionProof": [ + "the shared FetchDividendEvents contract remains signature-compatible and the strict normalizeEvent plus parseSSIDividendDates block is byte-for-byte unchanged from HEAD", + "the generic provider test verifies non-dividend rows do not leak into dividend discovery", + "focused stock and server tests passed", + "the full repository test suite passed", + "the stock package race test passed", + "vet, build, lint, and diff checks passed" + ], + "contractStatus": "CHANGED", + "blockingReasons": [] +} diff --git a/plans/260723-0852-stock-events-command/reports/harness/risk-gate.json b/plans/260723-0852-stock-events-command/reports/harness/risk-gate.json new file mode 100644 index 0000000..9502370 --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/harness/risk-gate.json @@ -0,0 +1,10 @@ +{ + "highRisk": true, + "reasons": [ + "an intentional new public Telegram command contract was added", + "the implementation refactors SSI pagination shared with existing dividend discovery" + ], + "autoStopRequired": true, + "humanApproved": true, + "largeDiff": true +} diff --git a/plans/260723-0852-stock-events-command/reports/harness/verification.json b/plans/260723-0852-stock-events-command/reports/harness/verification.json new file mode 100644 index 0000000..e191788 --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/harness/verification.json @@ -0,0 +1,64 @@ +{ + "commands": [ + { + "command": "go test -count=1 ./internal/modules/stock ./cmd/server", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "Focused stock-module and server command-discovery tests passed." + }, + { + "command": "go test -count=1 ./...", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "The complete Go test suite passed with no package failures." + }, + { + "command": "go test -race -count=1 ./internal/modules/stock", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "The stock package passed under the Go race detector." + }, + { + "command": "go vet ./...", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "Go vet completed without findings." + }, + { + "command": "go build ./...", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "All Go packages built successfully." + }, + { + "command": "golangci-lint run", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "golangci-lint reported zero issues." + }, + { + "command": "git diff --check", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "No whitespace errors were found; Git emitted only line-ending conversion notices." + }, + { + "command": "go test -count=1 -cover ./internal/modules/stock", + "status": "pass", + "exitCode": 0, + "timestamp": "2026-07-23T02:54:30.893Z", + "summary": "The stock package passed with 80.7% statement coverage." + } + ], + "beforeAfter": { + "before": "No public stock corporate-action lookup command existed.", + "after": "The command, metadata, raw SSI provider path, tests, and README are present; malformed optional dates remain displayable, cursor validation is isolated from raw output, and all automated gates pass." + } +} diff --git a/plans/260723-0852-stock-events-command/reports/pm-260723-1003-stock-events-command.md b/plans/260723-0852-stock-events-command/reports/pm-260723-1003-stock-events-command.md new file mode 100644 index 0000000..9f59ce2 --- /dev/null +++ b/plans/260723-0852-stock-events-command/reports/pm-260723-1003-stock-events-command.md @@ -0,0 +1,42 @@ +# PM Completion Report + +Plan: `plans/260723-0852-stock-events-command` + +## Phases + +- Phase 1 `Extend SSI Corporate Events`: completed +- Phase 2 `Add Telegram Command`: completed +- Phase 3 `Verify and Document`: completed + +## Tests + +- Focused stock tests: pass +- Command menu discovery test: pass +- `go test -count=1 ./...`: pass +- `go test -race -count=1 ./internal/modules/stock`: pass +- `go vet ./...`: pass +- `go build ./...`: pass +- `golangci-lint run`: pass +- `git diff --check`: pass + +## Docs + +- `README.md` updated for `/stock_events [days]` +- Plan text updated to reflect raw SSI display fields and private cursor handling +- Phase checklists and status synced to completed + +## Limitation + +- SSI lookup remains best-effort because the upstream corporate-actions endpoint is undocumented and can change without notice. + +## Blockers + +- None + +## Open Questions + +- None + +## Mappings + +- No unresolved task-to-phase mappings.