Commit (Revenue Planning) Service
Manages hierarchical revenue forecasting, team rollups, manager adjustments, and quota assignment across fiscal periods.
Service Overview
The Commit Service runs the forecasting cycle. Sales reps submit what they expect to close, managers roll those numbers up their team and adjust them, and the service holds quota targets alongside so the two can be compared.
From its README: it is “responsible for managing hierarchical revenue forecasting, team rollups, and manager adjustments… across multiple fiscal periods and cadences”.
Access control is hierarchy-based. A recursive SQL query walks the person tree to decide whose numbers you are allowed to see, so a manager sees their whole subtree and a rep sees only themselves. RDS credentials are pulled from SSM at runtime and cached for five minutes. Personal data is stored hashed and resolved through the DynamoDB PII vault.
It publishes, but listens to nothing
There are no EventBridge rules, SQS subscriptions, or event source mappings in this repo. Besides
the HTTP API, the only triggers are two cron schedules
(infrastructure/lambda/template.yaml:102-114):
| Schedule | Cron | Purpose |
|---|---|---|
AutoSubmitSchedule | cron(1 0 ? * MON *) | Auto-submit forecasts reps did not submit |
AutoAdjustSchedule | cron(1 0 ? * TUE *) | Auto-apply manager adjustments |
Events are published transactionally
publish_event re-raises ClientError rather than swallowing it
(api/events/eventbridge.py:172-195). If EventBridge rejects the publish, the surrounding database
transaction rolls back. That means you will not find a forecast row without its matching event —
useful to know when reconciling the two. Publishing is skipped for draft submissions.
The published payload is thinner than it looks
The service builds rich domain models — CommitSubmittedEvent carries commit_amount,
upside_amount, breakdown, target_quarter, cadence_label and more
(api/events/schemas.py:69-140).
But _enrich_event_detail (api/events/eventbridge.py:36-96) does not send those fields. It
builds a fresh audit envelope and copies across only timestamp, tenant_id, actor_id, status,
before, after, the error fields, and whichever of quota_internal_id / submission_id applies.
Everything else is dropped. On top of that, the domain models never populate before / after, so
those are always null.
Practical consequence: a consumer cannot learn the committed amount from the event. It receives the submission id and must call the API for the numbers.
One defect worth knowing about
A malformed detail-type. api/services/quota_service.py:444 passes
detail_type="QuotaAssigned" — no space — while the other three call sites (:303, :539, :886)
correctly pass "Quota Assigned". The lookup in _DETAIL_TYPE_MAP.get()
(api/events/eventbridge.py:53-55) falls through, so that bulk-create path emits an event with
detail-type: "QuotaAssigned", event_name: "rio.commit.commit.quotaassigned",
entity_type: "commit" and action: "quotaassigned". None of those are valid values in the audit
service’s enums, so the audit service rejects that event and quotas created through the bulk path
do not appear in the audit trail.
The bus name: a false alarm, resolved
infrastructure/template.yaml:174-176 defaults EventBusName to rio-commit-events, which is not
the shared bus. That default looks alarming, but it is dead config — it is never used.
samconfig.tmpl overrides it explicitly in all three environments:
| Environment | Value | Line |
|---|---|---|
| dev | dev-rio-events | samconfig.tmpl:97 |
| qa | qa-rio-events | samconfig.tmpl:198 |
| prod | prod-rio-events | samconfig.tmpl:307 |
These are literal strings, and the deployspec’s envsubst whitelist is only
'$DEPLOY_PREFIX,$ECR_IMAGE_URI' (cicd/deployspec.yaml:41), so nothing rewrites them. Every deploy
passes --config-env ${ENVIRONMENT}, selecting one of those three sections.
Commit events do reach the shared bus and the audit trail in every environment. The stale default is still worth changing so the template does not mislead the next reader, but it is not an operational problem.
The scheduled Lambda cannot publish
A related gap that is real: EventBusName is wired into the ECS module only
(infrastructure/template.yaml:340). It is not passed to LambdaModule (:257-280), so
${DeployPrefix}-auto-submit receives no EVENT_BUS_NAME environment variable
(lambda/template.yaml:84-100).
That Lambda is what auto-submits forecasts on Monday and auto-adjusts on Tuesday for people who did not act. Those automated submissions therefore produce no events — unlike the same action taken through the API. If you are reconciling forecast activity against the event stream, cron-driven submissions will be missing.
Data stores
Raw SQL, no ORM models or migrations. Names come from api/core/config.py:73-92.
Written by this service: fact_quota, fact_quota_audit, fact_forecast_submission,
fact_forecast_revision.
Read only: person, tenant_role, opportunity, hierarchy_revision, tag in Postgres;
fact_opportunity_history, fact_forecast_submission_history, dim_person_history and other history
tables in ClickHouse; the DynamoDB PII vault.
A note on this repo’s own docs
events/README.md in the service repo describes sources rio.commit.forecast and rio.commit.quota,
and event files named ForecastSubmitted.json / ForecastAdjusted.json / ForecastFinalized.json.
Those files do not exist — the real ones are CommitSubmitted.json, CommitAdjusted.json,
CommitFinalized.json — and the code uses a single source, rio.commit. The code is authoritative
and is what this catalog records.