Repository navigation
ENGAGE Rows Reference
Comment periods that ENGAGE owns: who owns which field, how a push finds its row, what a delete does, and what the two reconcile scripts do with them. Why: ADR-014-Sync-Out; queue and worker: Sync-Architecture; ingest request: Connecting-an-Application-to-DEMI.
An ENGAGE-owned row is a commentPeriods row with sourceSystem: 'engage' and an engagementId.
PUT /api/engage/engagements/{engagementId} (src/controllers/nosql/engage-comment-period.js) and
the Eagle mirror (src/controllers/nosql/comment-period.js) write it.
| Owner | Fields |
|---|---|
| ENGAGE |
projectId, engagementId, eagleProjectId, isMet, metURL, metURLAdmin, metBannerImageUrl, informationLabel, instructions, dateStarted, dateCompleted, dateAdded (set once, on create), isDeleted, isPublished, read, sources.engage, engagePushedAt, syncVersion, movedFromProjectId
|
| Eagle |
eagleId (after the first Eagle push), sources.eagle, eaglePushedAt, and the staff fields the Eagle mirror copies (staffFields in comment-period.js), except metURLAdmin
|
| Sync-out |
syncOut (per consumer: sentVersion, sentAt, status, error, projectId sent under, failedVersion after a failure, candidateEagleId on a conflict, droppedIds: the last 20 old Eagle ids a project move deleted, and a short-lived create claim), and eagleId once the consumer creates the Eagle period |
Rules that follow:
- An Eagle push to an ENGAGE-owned row writes only the Eagle column. It never changes dates, URLs,
labels,
read,isPublishedorisDeleted. -
readcomes fromengagement.isPublished:public,staff,sysadminwhen true;staff,sysadminwhen false. The parent project'sread[]then caps it.isPublishedis true when the storedreadholdspublic. - A notification parent is refused with
PARENT_KIND_UNSUPPORTED. Those engagements go to Eagle from ENGAGE directly. -
startandendarrive asYYYY-MM-DD HH:MM:SSwith no zone. DEMI reads them as UTC and storesdateStartedanddateCompletedas ISO 8601 withZ. ISO 8601 input also works, a time with no zone read as UTC; any other text is refused withPUSHED_DATES_INVALID. - Each source orders its own pushes: ENGAGE on
engagePushedAt, Eagle oneaglePushedAt. One source's clock never refuses the other's push. - Every ENGAGE write adds 1 to
syncVersion, even when no field changed. Sync-out compares it with what it last sent.engagePushedAtcannot serve, because a slug-only edit in ENGAGE leaves ENGAGE'supdated_dateas it was. - A comment period whose
readchanged level re-derives thereadof its comments.
An ENGAGE push writes to the first of these that exists:
- the row whose
engagementIdmatches the path, in the parent's partition first, then in any other; - the row whose
idequalsengagement.trackingId(an Eagle period ENGAGE already pushed, adopted on the first push); - the row
engage-<engagementId>, created when none exists.
A row id never changes after creation. If the row found already carries a different engagementId,
the push is refused with TRACKING_ID_CLAIMED.
A row found under another project has moved in ENGAGE. DEMI writes it under the new project, sets
movedFromProjectId, then deletes the old copy. A failed delete is logged, and the marker lets a
lookup read the two copies as one move, not a duplicate.
An Eagle push (PUT /api/eagle/commentperiods/{eagleId}) writes to the first of these:
- the row stored under the Eagle id;
- a row whose
eagleIdequals it, in the same partition first, then in any other; - an ENGAGE-owned row on the same
metURLthat has noeagleIdyet. A delete never joins this way.
Join 3 covers the echo of a period that sync-out has just created, which can reach DEMI before
sync-out stores the created period's eagleId. A row that already has another eagleId is never
taken.
Join 2 across partitions covers a project move, when the row sits under the new project but Eagle still files the period under the old one. The Eagle-owned fields are written where the row is.
With no match, the Eagle push creates an ordinary Eagle row, with two exceptions for a delete:
- A delete for a period DEMI holds no row for writes a staff-only tombstone and answers 200.
- A delete writes nothing, and answers 200 with
action: 'ignored', only when an ENGAGE row lists that Eagle id insyncOut.eagle.droppedIds. Sync-out's project-move path adds the old Eagle id there before it sends theDELETE, so the echo of that delete is ignored.
A delete of an Eagle id that the row no longer names changes nothing. It is the echo of sync-out removing the old period after a project move, and must not tie the row back to it.
A comment push whose period is stored as engage-<engagementId> finds the period by its eagleId.
Only an ENGAGE-owned row that a system read can see is taken this way, so a sealed or read-less
period still refuses its comments with 404. An eagleId stored on two period rows is a 500.
When ENGAGE sends engagement.isDeleted: true, DEMI keeps the row as a tombstone with isDeleted
true and read capped to level 2 (staff), and the comments under it follow. Sync-out sends DELETE
to eagle-api only when the row has an eagleId. A later Eagle push cannot clear the flag.
reconcile-eagle.js leaves ENGAGE-owned rows out of its comment period diff and out of
--drop-orphans, which deletes periods whose ENGAGE slug no longer resolves. The report counts them
as engageOwned. Drift classes: Eagle-Mirror-Reference#drift-classes.
node src/scripts/reconcile-engage.js [--repair] [--store] [--limit N]
It compares every ENGAGE-owned row with Eagle's copy and with ENGAGE's public engagement list.
Without --repair it writes nothing. --store saves the report under the cache id
reconcile-engage-report, and GET /api/admin/reconcile-engage (admin) returns it. --limit caps
the rows checked against Eagle.
| Report field | Meaning |
|---|---|
neverSynced |
A live row with no eagleId, other than the skipped ones. |
skipped |
A live row with no eagleId that sync-out will not create in Eagle: it has no metURL, or sync-out recorded skipped at its current syncVersion. |
missingInEagle |
The row has an eagleId and Eagle holds no such period. |
drift |
Eagle's copy differs in dateStarted, dateCompleted (compared to the minute), isPublished or metURL; or the row is deleted and Eagle still holds the period (isDeleted). |
duplicates |
An Eagle project with more than one period on the same metURL. |
conflicts |
Sync-out found an Eagle period on the row's metURL that DEMI already holds as an Eagle-owned row, and did not adopt it. Carries candidateEagleId. |
engageMissingInDemi |
ENGAGE lists an engagement under a project and DEMI has no row for it. Engagements under a notification are skipped. |
queued |
Rows --repair queued. |
warnings |
Checks that could not run, such as an unset setting or an HTTP error. |
--repair clears the row's sent version and queues the neverSynced, missingInEagle and drift
rows again, so DEMI's copy wins. Its messages carry repair: true, so the worker never drops one as
an already-failed redelivery. It queues nothing when SYNC_OUT_EAGLE_ENABLED is not true, and the
script never writes to Eagle itself. skipped, conflicts, engageMissingInDemi and duplicates
are report only.
ENGAGE's public list shows published engagements only, so a DEMI row missing from it is not checked.
Metadata reads that fail give one warning per HTTP status, listing the engagement ids. The ENGAGE
side is skipped when ENGAGE_API_BASE is unset, and the Eagle side when the sync-out Eagle settings
are unset.
The nightly timer reconcileEngage runs with --store and never with --repair. It exists only
when RECONCILE_ENGAGE_SCHEDULE is set.
| Setting | Meaning |
|---|---|
SYNC_OUT_QUEUE |
Queue name. Empty: no worker is registered and enqueue fails. |
SYNC_OUT_EAGLE_ENABLED |
true turns the Eagle consumer on. Anything else queues nothing for it. |
EAGLE_PROTECTED_API_BASE |
eagle-api's authenticated /api base. Not EAGLE_API_BASE, which is the public base the seed reads. |
EAGLE_KC_CLIENT_ID, EAGLE_KC_CLIENT_SECRET
|
Client-credentials login at the realm KEYCLOAK_URL and KEYCLOAK_REALM name. The client id is demi-sync-out; the secret is a Key Vault reference. |
EAGLE_ENGAGE_MILESTONE |
Milestone id sent on every Eagle write. eagle-api stores an invalid id when it is missing. |
ENGAGE_API_BASE |
ENGAGE's API, read by both reconcile scripts. Bicep param engageApiBase: test https://epic-engage-web-test.apps.gold.devops.gov.bc.ca/api, prod https://epic-engage-web-prod.apps.gold.devops.gov.bc.ca/api. |
RECONCILE_ENGAGE_SCHEDULE |
NCRONTAB schedule of the reconcileEngage timer. |
DEMI_ENGAGE_PRINCIPALS |
Registry principals allowed to call the ingest. Bicep param engagePrincipals, default apim:engage. Empty refuses everyone. |
- Environment Reality & Gotchas
- Architecture
- Seeding — filling Cosmos from Track and Eagle
- Frontend — React shell: screens, routes, sign-in gate
- Sources and Status — every source, one state
- Search & Retrieval — overview
- Search Index Reference
- Search Query Construction
- Deep Search Queries — passages, chunk filters, degraded modes
- Native List Reads — native list endpoints and mirror list reads
- Chunk Parent Fields — parent fields copied onto chunks
- Chunk Parent-Field Backfill — roll the fields out, repair
- Search Measurements
- Extraction Pipeline
- Extraction Quality — coverage, word-joining, tiling
- Identity Model — which id means what
- Attribute-Level Access — field-visibility levels, catalog
- Sync Architecture — how records get in
- Eagle Mirror Reference — mirrored kinds, refusals, drift classes
- ENGAGE Rows Reference — ENGAGE-owned comment periods, reconcile
- Public-Read Backfill — filling and repairing the mirror containers
-
Short Links —
/s/{code}redirect and/api/links -
Recent Uploads —
/api/documents/recent-uploads, eagle-public homepage panel - Reports — comment export and BCGW CSV reads
-
User Data — saved map areas, preferences,
/meroutes -
Bulk Download —
/api/bulk-downloads, zip jobs - Bulk Download: Operations — enable, poison-queue runbook
- Document Intake — upload link, malware scan, release
- Connecting an Application to DEMI
- Azure Environments
- API Gateway — APIM, Front Door edge secret, caller IP
- Document Backup — archive copy of originals, run and check
- Document Backup: Revert and Drill — put documents back from the archive
- PDF Titles — public name written into stored PDFs, run, restore
- Secret Sync — Key Vault, secret-sync Function app, required names
-
Operating DEMI in Azure — deploy, verify
- Operating the DEMI Data Plane — devbox access, probes, audit and cost reads
- Edge ban detector — hourly timer, modes, kill switch, soak and promote
-
CI Workflows — every GitHub Actions workflow, trigger, identity, roles
- CI Deploy Workflows — eagle-demi staging and production deploys
- CI Admin Frontend Workflows — eagle-demi-admin checks and deploys
- Release Process — cutting, tagging, shipping and rolling back a version
- Runbook: Search Outage — index/app schema drift, diagnose, fix, roll back
- BC Gov Landing Zone
- Business Rules
- EPIC.search Business Rules — where DEMI differs from EPIC.search
- 001: Typesense for DFL Search — superseded
- 002: Async Processing (MongoDB Queue) — superseded
- 003: Rule-Based Classification
- 004: Read ACL Authorization Model
- 005: Cosmos Full-Text Search Ruled Out — rejected
- 006: AI Summarization over BM25
- 007: Service-to-Service Credentials
- 008: Audit and Usage Analytics Store
- 009: Track Feed
- 010: Attribute-Level Access Control
- 011: Public Read Surface
- 012: Public Config Surface
- 013: Stored Project Summaries — proposed
- 014: Sync-out to Eagle
- 015: Malware scanning on intake
- Developer Brief: Digital File Library
- Developer Brief: AI Classifier Tool
- Developer Brief: EPIC System Integration
- Managing Government Information Policy 2022
- Guidelines on Documenting Government Decisions 2019
- AICT Guidelines
- Per-Document Data Format
- Per-Document Generator
- Document Types Draft
- DFL Reference Metadata
- Act and Legislation Full Text
- ORCS Filing Reference Sheet