1. What it is
feed-service is the place where a company talks to its own staff: an admin posts an announcement, employees see it in the mobile app, react to it, and the app records who has already seen what. It was also designed to celebrate achievements automatically — certificates, recognition, rewards, leaderboard wins — but that half of the product does not run today (see §8).
| Who uses it | Employees through the mobile app · admins through the admin web · users-auth-service for GDPR exports · kubelet probes |
| Runtime | FastAPI (pinned fastapi==0.109.2, feed-service/requirements.txt:1) · Deployment feed-service, replicas: 1 (feed-service/k8s/deployment.yaml:9) |
| Database | Postgres logical DB feed_db, sole writer (ARCHITECTURE.md §3.4) |
| Redis | users-auth-redis index 5 — visibility cache and author-profile cache (feed-service/k8s/configmap.yaml:7) |
| NATS streams | None owned at runtime. A SOCIAL stream would be created by EventProcessorService.start_consuming (app/services/event_processor.py:322), but that method is never called. |
| External APIs | None. One outbound in-cluster edge: users-auth-service batch profile lookup on the feed read path (app/clients/users_auth_client.py:33, app/services/post_service.py:206). ARCHITECTURE.md §3.2 still lists feed-service as "zero-outbound"; the code disagrees. |
| Entry points | 21 scanned HTTP routes (plus GET /v1/mobile/feed, which the scanner's empty-path rule missed) · 16 NATS consumer registrations over 8 subjects · 2 published subjects · 1 background job |
Read this before trusting the async half
Neither of the two NATS consumer groups in this service starts in the deployed image. The admin announcement path, the read path, reactions and view tracking are all real and working; everything that depends on incoming events is not. Details and evidence in §5 and §8.
2. Feature map
flowchart LR
ADMIN["Admin (admin web)"] --> ANN["Publish announcement"]
ADMIN --> MOD["Moderate posts"]
ADMIN --> CFG["Auto-post settings"]
EMP["Employee (mobile app)"] --> READ["Read the feed"]
EMP --> REACT["React to a post"]
EMP --> SEEN["Mark posts seen"]
EMP --> LIVE["Live updates (SSE)"]
UA["users-auth-service"] --> GDPR["GDPR export"]
ANN --> R1["POST /v1/admin/posts"]
ANN --> N1["publish training.notifications...feed.*"]
MOD --> R2["PATCH/DELETE /v1/admin/posts/*"]
CFG --> R3["/v1/admin/announcement-feed/config"]
READ --> R4["GET /v1/mobile/feed*"]
REACT --> R5["/v1/mobile/posts/{id}/reactions/*"]
SEEN --> R6["/v1/mobile/posts/*/viewed"]
LIVE --> R7["GET /v1/feed/sse"]
GDPR --> R8["GET /v1/internal/export/{user_id}"]
3. Features
Admin
Publish a company announcement live
An admin writes a title and body, chooses who should see it (whole company, a department, a location, a team, or a single person), and the post appears in those employees' feeds. For company-wide announcement types the service also fires a push notification so people see it without opening the app. If the notification fails the post is still saved — the announcement matters more than the push.
- Entry points
POST /v1/admin/posts- Touches
posts; publishestraining.notifications.ten.<tenant>.feed.<category>.<subtype>- Related
- Consumed by notification-worker for FCM delivery
- Evidence
- app/routes/admin_routes.py:51 · notification publish at app/routes/admin_routes.py:121 → app/events/nats_publisher.py:134
Moderate the wall — approve, pin, re-scope, delete suspect
Admins can approve a post that is waiting for review, pin an important one to the top, change
who it is visible to, or delete it. All four require an admin token carrying the
FEED_WRITE scope. The admin web app is not in this repository, so no caller can be
shown for these routes here.
- Entry points
PATCH /v1/admin/posts/{post_id}/approval·POST /v1/admin/posts/{post_id}/pin·PATCH /v1/admin/posts/{post_id}/visibility·DELETE /v1/admin/posts/{post_id}- Touches
posts; Redis visibility cache invalidation- Related
- Router-level
require_adminatapp/routes/admin_routes.py:26 - Evidence
- app/routes/admin_routes.py:223, :306, :153, :261 — searched the repo for each path string; only feed-service's own smoke tests match
Decide which events become celebration posts suspect
A settings screen where an admin turns automatic celebration posts on or off per event type — "post when someone earns a certificate", "post the weekly leaderboard top 3". The rows are stored and returned correctly. Today the switch controls nothing, because the component that would read it and write the posts never starts.
- Entry points
GET /v1/admin/announcement-feed/config·PATCH /v1/admin/announcement-feed/config/{event_subject}- Touches
auto_post_config,event_template_map,post_templates- Related
- Consumed by the dead auto-post pipeline — see §8
- Evidence
- app/routes/admin_routes.py:356, :395 · config table created in migrations/versions/305ebc942f22_add_auto_post_config_table.py:19
Employee (mobile)
Read the feed live
The main wall. The app asks for a page of posts, gets back only the posts that person is allowed to see, each already carrying the author's name and photo and that person's own reaction and seen state. Opening a single post works the same way. Author names come from users-auth-service in one batched call, cached in Redis so a busy feed does not hammer it.
- Entry points
GET /v1/mobile/feed·GET /v1/mobile/feed/{post_id}- Touches
posts,reactions,post_views,user_preferences; Redis; users-auth-service profile batch- Related
- Records views as a side effect — see "Remember what I have already seen"
- Evidence
- app/routes/feed_routes.py:63, :191 · client usage documented at docs/flutter_user_state_integration.md:30, :73
Home-screen highlights suspect
A short, cached strip of the most relevant posts for the app's home screen, so the landing view does not have to load the whole feed. No mobile client code or client-facing document in this repository names this path.
- Entry points
GET /v1/mobile/feed/home-highlights- Touches
posts; Redis; users-auth-service profile batch- Related
app/services/home_highlights_service.py- Evidence
- app/routes/feed_routes.py:131 — repo-wide search for "home-highlights" returns only bug-hunt-reports/feed-service.md:124 and to-be-reviewed/_summaries/feed-service.md:22, no caller
React to a post live
Tap to like, celebrate or otherwise react; tap the same reaction again and it is removed. The app can also ask which reactions this person left on a post, and for the per-type totals shown under it. The summary endpoint used to be readable without a token by anyone who knew a post id — that hole is closed.
- Entry points
POST /v1/mobile/posts/{post_id}/reactions/{reaction_type}live ·DELETEsame path suspect ·GET /v1/mobile/posts/{post_id}/reactionssuspect ·GET /v1/mobile/posts/{post_id}/reactions/summarysuspect- Touches
reactions,posts- Related
- Fixed IDOR:
bug-hunt-reports/feed-service.md#1/#7. Known wart: the toggle-off branch still answers 201 (#14). - Evidence
- app/routes/reaction_routes.py:16, :51, :86, :118 · POST documented for the app at docs/flutter_user_state_integration.md:93
Remember what I have already seen live
As a person scrolls, the app reports which posts were actually on screen, for how long, and how much of the post was visible — one at a time or as a batch when the screen closes. That is what keeps a post from being shown as new twice. Writes are idempotent, so a double pull-to-refresh no longer fails the whole feed load.
- Entry points
POST /v1/mobile/posts/{post_id}/viewed·POST /v1/mobile/posts/bulk/viewed·GET /v1/mobile/posts/{post_id}/view-statussuspect- Touches
post_views,user_preferences- Related
feed-service/VIEW_TRACKING_IMPLEMENTATION.md,feed-service/USER_STATE_IMPLEMENTATION.md- Evidence
- app/routes/view_tracking_routes.py:63, :25, :101 · both POSTs documented for the app at docs/flutter_user_state_integration.md:132, :158
Live feed updates while the app is open dead
The app can hold an open stream and receive new posts and celebrations the moment they happen, instead of pulling to refresh. The stream itself connects, authenticates and stays alive — but nothing is ever pushed into it, because the code that subscribes to the events is registered on a startup hook this app never runs. In practice a client receives one "connected" frame and a keepalive comment every 20 seconds, forever.
- Entry points
GET /v1/feed/sse- Touches
- In-process connection map only; no tables
- Related
- Cross-tenant leak in the broadcaster was fixed defensively even though the path is dormant —
bug-hunt-reports/feed-service.md#6; multi-device overwrite still open (#5/#12) - Evidence
- route app/routes/sse_routes.py:74; subscriptions registered at app/routes/sse_routes.py:212 via
@router.on_event("startup"), which does not fire under the pinned fastapi 0.109.2 / starlette 0.36.3 when a customlifespan=is supplied (app/main.py:43, :132) — reproduced empirically in bug-hunt-reports/feed-service.md #2
Internal (other services)
Hand over everything we hold about one person live
When someone exercises a GDPR access or portability request, users-auth-service asks every service that holds personal data for its slice and merges them. This is feed-service's slice: the person's posts, reactions, views and preferences. A person with no feed activity gets an empty but successful answer — "nothing here" is a valid reply to an access request.
- Entry points
GET /v1/internal/export/{user_id}(X-Oper-Key+X-Tenant-Id)- Touches
posts,reactions,post_views,user_preferences,comments- Related
ARCHITECTURE.md§3.2 edge H23 (GDPR export fan-out, CronJob)- Evidence
- app/routes/internal_routes.py:35 · caller users-auth-service/app/services/gdpr_export.py:156
Tell Kubernetes whether the pod can serve live
Two probes the cluster calls: one says the process is alive, the other says the feed read path can actually answer. Only Postgres decides the verdict — Redis and NATS are reported but never gate, because gating on NATS once took the whole service out of rotation.
- Entry points
GET /health/live·GET /health/ready·GET /health/startupsuspect- Touches
- Postgres
SELECT 1; cached Redis and NATS state - Related
- Rationale in the handler docstring at
app/core/health.py:22 - Evidence
- app/core/health.py:13, :22, :84 · probes wired at feed-service/k8s/deployment.yaml:58 (live) and :64 (ready); no manifest references /health/startup
Background
Heartbeat log line live
Every 30 seconds the pod logs that it is still running. Purely an operational breadcrumb — it reads nothing and writes nothing.
- Entry points
heartbeat_logger- Touches
- Nothing
- Related
- —
- Evidence
- app/main.py:83, started from the lifespan at app/main.py:63
Turn achievements into celebration posts dead
The designed centrepiece of the product: when someone finishes a module, earns a certificate, receives recognition, redeems a reward, tops the weekly leaderboard, or joins the company, the feed should celebrate it automatically using an admin-configurable template. The code to do this exists in full — templates, mapping, post writer, event publisher — and is never started. As deployed, only posts an admin writes by hand ever appear in the feed.
- Entry points
EventProcessorService.start_consuming(7 subjects, creates theSOCIALstream)- Touches
- Would write
postsand publishsocial.post.created - Related
bug-hunt-reports/feed-service.md#13 (deferred: needs a product/infra decision on the run model) ·ARCHITECTURE.md§4.1(5)- Evidence
- class defined at app/services/event_processor.py:23; repo-wide search for
EventProcessorServicereturns only that definition and a prose mention in app/services/gdpr_export_service.py:81 — no instantiation. The lifespan (app/main.py:43-80) starts only Redis, the HTTP client and the heartbeat.
4. API reference
Every route the scanner found. "Callers" lists only non-test callers: feed-service's own
tests/ smoke scripts hit most of these paths and do not count as evidence.
One real route the scan output does not contain
GET /v1/mobile/feed — the main feed read, mounted as the router's empty path
(app/routes/feed_routes.py:63 under the /v1/mobile/feed prefix at
app/main.py:158). The scanner emits no row for an empty route path, so it is
absent from feed-service.json and from the table below. It is
live: the mobile client contract documents it at
docs/flutter_user_state_integration.md:30, and it is the busiest endpoint in the
service. Treat the table below as "the 21 scanned routes", not "the whole surface".
| Method | Path | Auth | Feature | Callers | Verdict |
|---|---|---|---|---|---|
| GET | /health/live | none | Probes | kubelet — k8s/deployment.yaml:58 | live |
| GET | /health/ready | none | Probes | kubelet — k8s/deployment.yaml:64 | live |
| GET | /health/startup | none | Probes | none — no startupProbe in k8s/deployment.yaml | suspect |
| POST | /v1/admin/posts | admin JWT + FEED_WRITE | Publish announcement | admin web (not in repo) | suspect |
| PATCH | /v1/admin/posts/{post_id}/visibility | admin JWT + FEED_WRITE | Moderate | admin web (not in repo) | suspect |
| PATCH | /v1/admin/posts/{post_id}/approval | admin JWT + FEED_WRITE | Moderate | admin web (not in repo) | suspect |
| POST | /v1/admin/posts/{post_id}/pin | admin JWT + FEED_WRITE | Moderate | admin web (not in repo) | suspect |
| DELETE | /v1/admin/posts/{post_id} | admin JWT + FEED_WRITE | Moderate | admin web (not in repo) | suspect |
| GET | /v1/admin/announcement-feed/config | admin JWT | Auto-post settings | admin web (not in repo) | suspect |
| PATCH | /v1/admin/announcement-feed/config/{event_subject} | admin JWT + FEED_ANNOUNCE | Auto-post settings | admin web (not in repo); the setting has no runtime effect while the auto-post pipeline is dead | suspect |
| GET | /v1/mobile/feed/{post_id} | user JWT | Read the feed | mobile app — docs/flutter_user_state_integration.md:73 | live |
| GET | /v1/mobile/feed/home-highlights | user JWT | Home highlights | none found | suspect |
| POST | /v1/mobile/posts/{post_id}/reactions/{reaction_type} | user JWT | React | mobile app — docs/flutter_user_state_integration.md:93 | live |
| DELETE | /v1/mobile/posts/{post_id}/reactions/{reaction_type} | user JWT | React | none found (the POST is a toggle, so the app may never need this) | suspect |
| GET | /v1/mobile/posts/{post_id}/reactions | user JWT | React | none found | suspect |
| GET | /v1/mobile/posts/{post_id}/reactions/summary | user JWT | React | none found | suspect |
| POST | /v1/mobile/posts/{post_id}/viewed | user JWT | Seen tracking | mobile app — docs/flutter_user_state_integration.md:132 | live |
| POST | /v1/mobile/posts/bulk/viewed | user JWT | Seen tracking | mobile app — docs/flutter_user_state_integration.md:158 | live |
| GET | /v1/mobile/posts/{post_id}/view-status | user JWT | Seen tracking | none found | suspect |
| GET | /v1/feed/sse | user JWT | Live updates | no in-repo caller; and nothing can ever be pushed into the stream (see §8) | dead |
| GET | /v1/internal/export/{user_id} | X-Oper-Key + X-Tenant-Id | GDPR export | users-auth-service/app/services/gdpr_export.py:156 | live |
5. Async contracts
Consumes
Sixteen registrations: the same eight subjects are registered twice, once by the SSE router and once by the auto-post pipeline. Neither registration site executes in the deployed image, so every row below is dead — but for two different reasons, and each reason is independent of whether the subject has a producer.
| Subject | Stream | Durable | Published by | Feature | Verdict |
|---|---|---|---|---|---|
social.post.created | would-be SOCIAL | announcement_sse_social_post_created (sse_routes.py:197) | feed-service itself (event_processor.py:130) — which also never runs | Live updates | dead |
social.post.created | SOCIAL (created at event_processor.py:322) | social-announcement-service__social_post_created | feed-service itself (event_processor.py:130) | Auto-post | dead |
training.certificates.module_completed | MICRO_LEARNING | announcement_sse_training_certificates_module_completed | micro-learning-service-v2/app/events/events.py:257 | Live updates | dead |
training.certificates.module_completed | MICRO_LEARNING | social-announcement-service__training_certificates_module_completed | micro-learning-service-v2/app/events/events.py:257 | Auto-post | dead |
training.module.published | MICRO_LEARNING | announcement_sse_training_module_published | micro-learning-service-v2/app/events/events.py:55 | Live updates | dead |
training.module.published | MICRO_LEARNING | social-announcement-service__training_module_published | micro-learning-service-v2/app/events/events.py:55 | Auto-post | dead |
training.module.completed | MICRO_LEARNING | announcement_sse_training_module_completed | micro-learning-service-v2/app/events/events.py:80 | Live updates | dead |
training.module.completed | MICRO_LEARNING | social-announcement-service__training_module_completed | micro-learning-service-v2/app/events/events.py:80 | Auto-post | dead |
users.created | AUTH | announcement_sse_users_created | users-auth-service/app/routes/users.py:222 | Live updates | dead |
users.created | AUTH | social-announcement-service__users_created | users-auth-service/app/routes/users.py:222 | Auto-post (welcome post) | dead |
recognition.created | would-be SOCIAL | announcement_sse_recognition_created | nobody | Live updates | dead |
recognition.created | would-be SOCIAL | social-announcement-service__recognition_created | nobody | Auto-post | dead |
reward.redeemed | would-be SOCIAL | announcement_sse_reward_redeemed | nobody | Live updates | dead |
reward.redeemed | would-be SOCIAL | social-announcement-service__reward_redeemed | nobody | Auto-post | dead |
leaderboard.weekly.finalized | would-be SOCIAL | announcement_sse_leaderboard_weekly_finalized | nobody | Live updates | dead |
leaderboard.weekly.finalized | would-be SOCIAL | social-announcement-service__leaderboard_weekly_finalized | nobody | Auto-post | dead |
Correcting ARCHITECTURE.md §4.1(5)
§4.1(5) says feed's "4 social subjects have no producer". Verified subject by subject with a
repo-wide search: three have no producer anywhere —
recognition.created, reward.redeemed and
leaderboard.weekly.finalized. The fourth, social.post.created, does
have a producer, but it is feed-service's own _publish_post_event
(app/services/event_processor.py:130), reachable only from the auto-post pipeline
that never starts — so at runtime it is equally silent. The three unproduced subjects match
the SDD's expected senders — "recognition-service", "rewards-service", "leaderboard-service"
(docs/sdd.md:47-51) — none of which exist in the platform inventory
(ARCHITECTURE.md §2).
Publishes
| Subject | Consumed by | Feature | Verdict |
|---|---|---|---|
training.notifications.ten.*.feed.*.*app/events/nats_publisher.py:134, called from app/routes/admin_routes.py:121 |
notification-worker, pull consumer notification_worker_feed on MICRO_LEARNING, filter training.notifications.ten.*.feed.> — the filter's trailing > matches the two extra tokens, so this one is not a wildcard mismatchnotification-worker/app/services/notification_service.py:2322-2333 |
Publish announcement → push notification | live |
social.post.createdapp/services/event_processor.py:130 via _publish_post_event (:280) |
Only feed-service's own two registrations, both dormant. No other service in the repo subscribes to social.>. |
Auto-post | dead |
Background jobs
| Job | Schedule | What it does | Verdict |
|---|---|---|---|
heartbeat_loggerapp/main.py:83 | Every 30 s, for the pod's lifetime | Logs a liveness breadcrumb | live |
EventProcessorService.start_consumingapp/services/event_processor.py:299 | Would run forever once started | Creates the SOCIAL stream, binds 7 durables, writes celebration posts | dead |
6. Data it owns
Postgres logical DB feed_db. feed-service is the only writer; no other service
reads or writes these tables (ARCHITECTURE.md §3.4).
| Table | What it holds | Written by |
|---|---|---|
posts | Every feed item: title, body, type, author, visibility scope and optional target, pinned/approved flags, media URI. A DB CHECK enforces that department/location/team posts carry a target and company/private posts do not. | Admin create / moderate routes; would also be written by the dead auto-post pipeline migrations/versions/001_initial_schema.py:94 |
reactions | One row per person per reaction type per post. | Reaction routes migrations/versions/001_initial_schema.py:128 |
comments | Comment rows. Designed in the SDD; no HTTP route in this service reads or writes them today. | Nothing migrations/versions/001_initial_schema.py:148 · model app/models/comment.py:6 |
post_views | Who saw which post, when, for how long, and how much of it was on screen. UNIQUE(user_id, post_id), written with ON CONFLICT DO NOTHING. | View-tracking routes and the feed read path app/models/user_state.py:9 · migrations/versions/add_user_state_tracking.py |
user_preferences | Per-person feed preferences and the soft-delete marker used by the GDPR path. | Feed read path app/models/user_state.py:22 · migrations/versions/add_user_prefs_deleted_at.py |
post_templates | Reusable wording for automatic celebration posts. | Seeded by migration; read by the dead auto-post pipeline migrations/versions/001_initial_schema.py:73 |
event_template_map | Which event subject produces which post type and template. | Seeded by migration; re-pointed by migrations/versions/fix_feed_category.pymigrations/versions/001_initial_schema.py:167 |
auto_post_config | Per-tenant on/off switch per event subject for automatic posts. | Admin auto-post settings routes migrations/versions/305ebc942f22_add_auto_post_config_table.py:19 |
No cross-service writes in either direction. The only shared infrastructure is the Redis
instance (users-auth-redis), where feed-service uses its own index 5.
7. Dependencies
flowchart LR
MOB["Mobile app"] --> FS["feed-service"]
ADM["Admin web"] --> FS
UA["users-auth-service (GDPR CronJob)"] --> FS
K8S["kubelet probes"] --> FS
FS --> UA2["users-auth-service (profile batch)"]
FS --> PG[("feed_db")]
FS --> RD[("Redis index 5")]
FS --> NW["notification-worker (via NATS)"]
ML["micro-learning-service-v2"] -.->|"events nobody consumes"| FS
Dotted edge: micro-learning publishes the training subjects feed-service registers for, but the registrations never execute, so no message is ever delivered.
8. Dead-code verdicts
Every entry point with no in-repo caller. Deleting is a separate decision — see the hub roll-up.
| Entry point | Kind | Verdict | Evidence |
|---|---|---|---|
EventProcessorService — the whole auto-post pipeline, including the SOCIAL stream creation and 7 durables |
Background loop | dead |
No caller: class defined at app/services/event_processor.py:23; a repo-wide
search for EventProcessorService finds only that definition plus a prose
reference in app/services/gdpr_export_service.py:81. No instantiation anywhere.Unreachable in principle: the only process entry point is uvicorn app.main:app, whose lifespan (app/main.py:43-80) starts Redis, the HTTP
client and the heartbeat and nothing else; there is no second entry point, no worker
Deployment and no CronJob in feed-service/k8s/. Matches bug-hunt-reports/feed-service.md #13.
|
subscribe_to_events() / handle_nats_message() — the 8 SSE push subscriptions |
NATS consumers | dead |
No caller: the only invocation is the router-level
@router.on_event("startup") at app/routes/sse_routes.py:212.Unreachable in principle: the app is constructed with a custom lifespan= (app/main.py:132, function at :43). Under the pinned
fastapi==0.109.2 / starlette==0.36.3
(feed-service/requirements.txt:1) a custom lifespan replaces the default lifespan context
that would run on_startup handlers, so the hook never fires — reproduced in an
isolated venv against the pinned versions, bug-hunt-reports/feed-service.md #2.Latent risk, not a fix: a FastAPI upgrade past that boundary would switch these subscriptions on. The tenant filter in broadcast() was therefore hardened
anyway (#6, app/routes/sse_routes.py:31-45). Two further hazards survive in that state: all
eight durable names are derived from the subject alone (app/routes/sse_routes.py:197), so a
second replica would bind the same durables; and connections are keyed by
user_id only (app/routes/sse_routes.py:25), so a second device displaces the
first (#5/#12).
|
GET /v1/feed/sse |
HTTP route | dead |
No caller: repo-wide search for /v1/feed/sse matches only
feed-service's own smoke scripts (tests/smoke_test.py:293, tests/smoke_reactions_sse.py:209),
the gzip exclusion list (app/main.py:111) and the bug reports.Unreachable in principle as a feature: the route itself answers, but the only writer into its queues is feed_manager.broadcast, called exclusively from
handle_nats_message (app/routes/sse_routes.py:145, :164), which is registered
only by the startup hook that never runs. A connected client can therefore receive nothing
but the initial CONNECTED frame (app/routes/sse_routes.py:87) and a keepalive
comment every 20 s (app/routes/sse_routes.py:63).
|
recognition.created, reward.redeemed, leaderboard.weekly.finalized |
NATS subjects (consumed) | dead |
No publisher: a repo-wide search for each subject string returns only
feed-service's own consumer lists, mapper and template seed rows
(app/routes/sse_routes.py:188-190, app/services/event_processor.py:312-314,
app/services/event_mapper.py:32-34, migrations/versions/001_initial_schema.py:178-180) plus
feed-service tests. No publish( call anywhere in the repo names them.And no producer can exist: the SDD names recognition-service, rewards-service and leaderboard-service as the senders (docs/sdd.md:47-51); none of those services is in the platform inventory (ARCHITECTURE.md §2). No stream in ARCHITECTURE.md §3.3 covers recognition.>, reward.> or
leaderboard.> either — only feed's own never-executed
add_stream("SOCIAL", …) would create one.
|
social.post.created (published) |
NATS subject (published) | dead |
Publisher unreachable: published at app/services/event_processor.py:130 via
_publish_post_event (:280), only from process_event inside the
never-instantiated EventProcessorService.No live consumer: the only subscribers are feed-service's own two registrations (app/routes/sse_routes.py:186, app/services/event_processor.py:308-315), both dead for the reasons above. A repo-wide search finds no other service subscribing to social.post.created or a social.> filter.
|
comments table and app/models/comment.py |
Data model | dead |
Table created at migrations/versions/001_initial_schema.py:148 and modelled at
app/models/comment.py:6, but no route, service or repository in app/ reads or
writes it — commenting is an SDD goal (docs/sdd.md:33) that was never routed.
|
GET /health/startup |
HTTP route | suspect | app/core/health.py:84. feed-service/k8s/deployment.yaml declares only a livenessProbe (:58) and a readinessProbe (:64) — no startupProbe. Reachable by anything that can reach the pod, so not provably unreachable; it is simply unused by the cluster. |
All /v1/admin/* routes · /v1/mobile/feed/home-highlights · GET .../reactions · GET .../reactions/summary · DELETE .../reactions/{reaction_type} · GET .../view-status |
HTTP routes | suspect | Searched the whole repo for each path string and for the handler names. Matches come only from feed-service's own tests/ scripts and audit documents. The admin web and the mobile app are not in this repository, so absence of an in-repo caller says nothing about production traffic — resolving these needs either the mobile/admin client repos or access-log sampling per path. |
9. Sources
feed-service/docs/sdd.md— the original Social & Announcements design: goals, intended event sources and published events.feed-service/docs/flutter_user_state_integration.md— the mobile client contract; the only in-repo document that names concrete mobile paths.feed-service/docs/testing_checklist.md,feed-service/docs/TEST_RESULTS.md— behavioural expectations per endpoint and event.feed-service/USER_STATE_IMPLEMENTATION.md,feed-service/VIEW_TRACKING_IMPLEMENTATION.md— the user-state and view-tracking design notes.bug-hunt-reports/feed-service.md— 14 findings; #2, #5, #6, #12, #13 are load-bearing for this page.ARCHITECTURE.md§2 (inventory), §3.2 (HTTP edges), §3.3 (streams), §3.4 (databases), §3.5 (Redis map), §4.1(5) (the feed defect this page narrows).- Code read directly:
app/main.py,app/core/health.py,app/routes/*.py,app/services/event_processor.py,app/events/nats_publisher.py,app/clients/users_auth_client.py,migrations/versions/,k8s/. - Cross-service verification:
micro-learning-service-v2/app/events/events.py,users-auth-service/app/routes/users.py,users-auth-service/app/services/gdpr_export.py,notification-worker/app/services/notification_service.py.