Skip to content

TMS v2 phase 1 (platform): as-built spec ​

This folder describes the phase-1 platform code as it stands in the repos: schema, API, conditions, flows, jobs, settings, concurrency and gaps. It goes deep enough to extend or rebuild each part, and it exists so phases 2–7 keep the same pattern. For a one-meeting overview of the whole system, read the v2 reference instead.

Start with 00-patterns. It covers what every module shares. Then open the part you are working on.

Files ​

filepartread it for
00-patterns.mdshared patternsrepo layout, module skeleton, registries, base service/schema, controller and error conventions, naming, web conventions, checklist for a new module
02-auth.mdauthlogin and session, /auth/me, roles, the permission registry (55 keys), guard, store scope (X-Store-Id), users admin, Roles/Users web pages
03-logs-and-settings.mdlogs, activity logs, settingswhat gets logged and how long it is kept, record history, the settings module (6 areas, 31 keys), the foundation simplify cuts
04-jobs-engine.mdjobs enginefast and long lanes, leases, retry, DEAD rows, schedules, admin API, Kafka as a job target
05-kafka.mdKafkaoutbound DO events, inbound consume (dedup, claim, DLQ), every topic
06-media.mdmedia and attachmentsS3 storage setting, presigned upload, attachment lifecycle, sweep, hidden-field guards
07-template-export.mdtemplates and exportprint/email/SMS render, script runner sandbox, field catalog (6 entities), export config

Each part file uses the same 13 sections: summary · file map · data model · API · conditions · states and flows · jobs/events · settings and env · concurrency and recovery · web · tests · gaps · how to add the next one.

Which code this describes ​

partcore-v2store-v2merged?
03 to 07origin/main-v2-prototype 3d70475aorigin/main-v2-prototype 0851a3ayes
02 authfeature/auth 9d9422c (base 3d70475)feature/auth d9fd082 (base 0851a3a)no, still in its own workspace (tms-v2-auth)

Because auth is not merged yet, files 03–07 show the routes guarded by @Roles([admin]). The setting.* permission keys in 02-auth exist only on feature/auth. All facts come from reading the code. Nothing was run, so line numbers and behaviour under load are not runtime-checked.

Problems found while writing (code, not doc) ​

Each file's "Gaps" section has the full list. These are the ones that break something today:

#whereproblemfile
0every search endpointThe client-built Mongo filter (convertFilter) goes straight into find(). Any operator or field gets through, $where and hidden fields included, and the keyword is used as an unescaped regex. Only the script runner blocks operators.00
1store-v2 Setting pagesStorage, Script settings and Outbound events pages call core routes that were removed in 3dd1c5a (/storage-settings, /script_settings, /outbound_events). They now get 404.03, 05, 06, 07
2storage defaultBucket is empty and allowed_extensions is [] by default, so every upload fails (503 or EXTENSION_NOT_ALLOWED). Because of #1 it cannot be fixed from the UI.03, 06
3auth web keysweb:src/constants/permission-keys.ts is hand-written. 9 of its 24 keys don't exist in core, so even a full admin loses 8 Setting menu items.02
4auth user statesWeb expects active/locked/disabled, core sends active/locked_out/inactive/staged. Unlock never shows and Enable/Disable shows the wrong label.02
5Kafka DO consumerEvery error, including a temporary Mongo error, is treated as permanent, and the message goes to the DLQ.05
6Kafka off switchThe enabled flag (global and per event) is never checked on send, so turning Kafka off doesn't stop DO-update messages.04, 05
7Kafka controller/v1/kafka/status and /test-connection have no role guard. Any logged-in user, a driver included, can make a pod connect to any broker.03, 05
8exportExport run is not built: configs can be saved, but nothing produces a file.07

Pattern drift, to settle before phase 2 so new modules have one rule to follow (detail in 00-patterns §11):

  • List responses come in 5 different shapes.
  • Concurrency control comes in 3 kinds: rev, revision, and none (last write wins).
  • not_found is returned as 400 in some places and 404 in others.
  • A duplicate key returns 500, 400 or 409 depending on the module.
  • Route names mix singular and plural.

Smaller items found:

  • Nothing is ever deleted: DEAD jobs, DLQ rows and orphaned S3 objects stay forever.
  • No schedule is declared anywhere.
  • The first settings save freezes the code defaults into the record.
  • Liquid output in templates is not HTML-escaped.