- Remove ReservedConcurrentExecutions: 1 (account lacks unreserved
concurrency headroom; deploy rolled back). Accept best-effort
counter semantics as documented.
- Add /stats to aws/telegram-commands.json so it appears in the
Telegram client's / autocomplete after the next deploy.
Implement a new stats module for the Telegram bot that tracks per-command usage with persistent KV storage. The module provides a /stats command displaying usage sorted by popularity with a 4096-byte Telegram message cap. Includes CommandHook integration for post-dispatch tracking via background goroutine (2s bounded context), proper test coverage, and registry initialization. Updated server config with stats factory and reserved concurrent execution control to prevent TOCTOU issues.
YAML 1.1 implicit conversion: bare 'OFF' parses as boolean false.
AWS::Scheduler::Schedule.FlexibleTimeWindow.Mode requires the string
'OFF' or 'FLEXIBLE'. CFN's EarlyValidation hook rejects the boolean,
causing 'Failed to create changeset' on every deploy attempt.
Verified by manual create-change-set against the SAM-packaged
template: Mode: false -> FAILED; Mode: "OFF" -> CREATE_COMPLETE
with 4 changes (BotFunction modify, LolscheduleDailyPushSchedule add,
SchedulerExecutionRole policy modify, MonthlyBudget remove).
Supersedes 9050d6b (never deployed). EventBridge ApiDestination has no
free tier — $0.20/M, rounds to $0 at our 30 invokes/month but still a
billable line item, which violates the project's hard free-tier rule.
Replace 4 resources with 2:
- drop: CronConnection, LolscheduleDailyPushApiDestination,
EventBridgeInvokeRole, LolscheduleDailyPushRule
- add: SchedulerExecutionRole, LolscheduleDailyPushSchedule
Schedule fires cron(0 1 * * ? *) at Lambda directly (free tier:
14M invocations/mo). Target.Input carries a synthetic Function URL v2
event so Lambda Web Adapter proxies it to /cron/lolschedule_daily_push
unchanged. The X-Cron-Token header is interpolated from the NoEcho
CronSharedSecret parameter.
Accepted trade-off: token value is visible to anyone with
scheduler:GetSchedule on the schedule. IAM gates that; the project's
security policy treats secret-in-Scheduler-Input as acceptable.
CloudFormation's AWS::Scheduler::Schedule Target schema has no property
for HTTPS universal invocation (URL, method, headers) — confirmed
against AWS docs. Switch to the legacy EventBridge Rule path which
supports HTTP targets natively via ApiDestination:
- AWS::Events::Connection: API_KEY auth, presents X-Cron-Token header.
ApiKeyValue stored in EventBridge service-linked secret on stack
update (no per-invoke SSM fetch, AWS-managed secret fees).
- AWS::Events::ApiDestination: POST to ${FunctionUrl}cron/lolschedule_daily_push.
- AWS::Events::Rule: cron(0 1 * * ? *) — daily 01:00 UTC / 08:00 ICT.
Targets ApiDestination with retry x2, 600s max age, DLQ to CronDLQ.
- EventBridgeInvokeRole replaces SchedulerExecutionRole (events.amazonaws.com
principal, events:InvokeApiDestination scoped to this destination only).
NoEcho CronSharedSecret CFN parameter restored; GHA fetches the SSM
SecureString and passes via --parameter-overrides so the value never
appears in template source or stack events.
Free-tier preserved: 1 invocation/day, well under EventBridge Rules +
ApiDestinations free quotas.
Reverts 1678070 + 06d430f. Both CFN deploys failed at changeset
validation; prod stack was never mutated. Approach A
(AWS::Scheduler::Schedule with arn:aws:scheduler:::http-invoke target)
is unimplementable in pure CloudFormation — the Target schema has no
property for HTTP endpoint/method/headers, regardless of name.
Replacement landing in a follow-up commit.
Restored: plans/reports/brainstorm-260517-1411-eventbridge-schedule-fix.md
(useful design context even though Approach A invalidated).
CloudFormation rejects {{resolve:ssm-secure}} inside
AWS::Scheduler::Schedule HttpInvokeArgs.HeaderParameters (documented
property-allowlist limitation). Add a NoEcho CronSharedSecret parameter
and fetch the SSM SecureString in CI, then pass via --parameter-overrides.
Lambda runtime SSM-fetch flow unchanged.
Adds AWS::Scheduler::Schedule resource invoking the Function URL via
HTTPS universal target (arn:aws:scheduler:::http-invoke) at 01:00 UTC
daily. Token sourced from SSM via {{resolve:ssm-secure}}; failures
retried twice and DLQ'd to existing CronDLQ. Closes the deferred
phase-05 of 260510-0234-pre-deploy-wrapup.
Rename:
- Go module github.com/tiennm99/miti99bot-go → github.com/tiennm99/miti99bot
- CloudFormation stack miti99bot-aws-port → miti99bot
- Drop "port", "Cloud Run", "GCP", "cutover", "Phase NN" framing from
active code and docs — project reads as canonical AWS-Lambda from now on.
AWS deploy guide + flow fix:
- New docs/deploy-aws-free-tier-guide.md — Ubuntu 24.04 ARM64 onboarding
with project-local venv (pip awscli + sam-cli), SSM secrets via read -s,
idempotent OIDC provider + role creation, $1 budget alarm.
- Drop sam build from the pipeline — provided.al2023 + makefile builder
expects a Makefile in CodeUri (build/lambda/, the output dir), so the
step always fails. sam deploy --template-file template.yaml now reads
the raw template and zips build/lambda/ directly.
- Rollback section rewritten — use continue-update-rollback /
cancel-update-stack / git-SHA redeploy. Drop the broken
--use-previous-template recipe.
- DynamoDB free-tier row corrected (on-demand is 2.5M read / 1M write
request units, not 25 RCU/WCU).
Updated:
- README.md fully rewritten (drops port/legacy framing, lists modules,
points new users at the free-tier guide).
- aws/README.md retitled "AWS account setup", phase numbers stripped.
- Makefile / .github/workflows/deploy.yml — sam deploy flow.
- samconfig.toml — stack_name = "miti99bot".
- Go comments — Cloud Run → Lambda, Cloud Scheduler → EventBridge
Scheduler, Cloud Logging → CloudWatch Logs.
- Struct field GCPProject → FirestoreProject (env GOOGLE_CLOUD_PROJECT
unchanged).
Plus advisory reports under plans/reports/ from the code-reviewer +
researcher passes that informed the fixes.
Verified: go vet ./..., go build ./..., go test ./... all green.
Removes six modules (loldle-ability/emoji/quote/splash, semantle, doantu)
and prunes the framework deps that were only there to serve them:
- ai.Embedder + Client.Embed + embeddingModel const (semantle only)
- Deps.Embedder + BuildOptions.Embedder
- Deps.Env + Build(env) param + ModuleEnv config field + PHOW2SIM allowlist (doantu only)
- internal/champname package (loldle now owns its lookup helpers directly)
- template.yaml: Phow2simAPIURL parameter + PHOW2SIM_API_URL Lambda env
Active catalog: util, misc, wordle, loldle, lolschedule, twentyq, trading.
go build / vet / test all pass.