docs(stock): add stock-events plan artifacts

This commit is contained in:
2026-07-23 10:05:44 +07:00
parent 7123ea77c3
commit 7c5da0f7f6
10 changed files with 413 additions and 0 deletions
@@ -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 todays 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.
@@ -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 <ticker> [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 `<ticker> [days]` and a concise description compatible with `/help` and Telegrams 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 repos 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: <id>` 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: "<ticker> [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 <ticker> [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 1s additive provider can remain unused or be reverted separately without data cleanup.
@@ -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 <ticker> [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 <ticker> [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.
@@ -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 <ticker> [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.
@@ -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": []
}
@@ -0,0 +1,41 @@
{
"skill": "ck:cook",
"mode": "interactive",
"task": "Add /stock_events <ticker> [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 <ticker> [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."
}
@@ -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 <ticker> [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": []
}
@@ -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
}
@@ -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."
}
}
@@ -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 <ticker> [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.