Repository navigation
feat: Jira row on the project page (WP-C) - #247
Merged
Merged
Conversation
selluxhenner
added a commit
that referenced
this pull request
Oct 29, 2026
WP-A of `docs/plans/2026-10-29_jira-in-app.md`: the Jira sync engine, moved out of n8n into the app. Builds on the contract (#245), the admin page (#246) and the lead UI (#247), which are all on main. **What it does** - `server/jira-sync.ts`: a loop once a minute (`JIRA_SYNC_INTERVAL_MS` overrides it, the e2e run uses 2000) for each company with Jira on. It takes the lease, pushes, then pulls, and records the result on the Integration row. An hourly reconcile reads every linked issue. `kickJira(slug)` starts a round early; it is called after a person's project event (and a `case.decided`), and from the webhook. - `server/jira/round.ts`: one round. It only knows a `RoundStore`, so the unit tests run whole rounds against the fake Jira with an in-memory log. - Pure core in `features/integrations/jira/`: `map` (issue key to project/step, sync mode), `plan` (state and events to jobs), `pull` (Jira JSON to events), `status` (transition picking, hopping through In Progress when the workflow has no direct way), `adf`. - `server/jira/cloud.ts` (API v3, Basic auth, `/search/jql` with `nextPageToken`, one inline wait on a short 429), `dc.ts` (stub that says "not supported yet"), `test.ts` (`testJira`, a failed call is a failed check). - `POST /api/[company]/integrations/jira/webhook`: HMAC check, then only `kickJira`. The header name (`x-hub-signature`) is marked TBD in `server/jira/webhook.ts`; confirm it on the live site. - One-line hooks in `instrumentation.ts` and `server/actions/events.ts`. **Echo-loop rules, as built** 1. Push acts only on human actors (`isSystemActor`), and the last move of a step in a batch decides. 2. Pull skips changes whose latest history is by the integration account, and writes nothing when NextUp already shows the state (Blocked against In Progress counts as the same). 3. idemKeys `jira:epic:<projectId>`, `jira:issue:<stepId>`, `jira:<key>:status:<historyId>`, `jira:<key>:summary:<historyId>`, `jira:added:<key>`, `jira:comment:<id>`. Creates look for the label first and adopt an existing issue. 4. At most 20 creates per round. A round with nothing to do writes no events and makes no write call to Jira (`jira-sync.test.ts` runs 5 silent rounds after each scenario). **Decisions worth a look** - The first round after switch-on does not replay old events (`pulledAt` null and `pushedSeq` 0); creates come from the state, not the cursor. - Imported comments read `Jira NUP-2: <text>` (cut to 2000), with no author and mentions as `@someone`. - A failed job that Jira refused for good (no transition, a 4xx) is reported as `lastError` but does not hold the cursor; transient failures (401, 429, 5xx, no answer) end the round and move nothing. - Added beyond the file list: `server/jira/round.ts` and `server/jira/webhook.ts`, so the engine and the HMAC check are testable without a database. - Not covered here: the e2e-db spec and the live test on `nextup-test.atlassian.net`; and the Labels check in "Test connection" can only prove the create/edit permission and that JQL on labels parses, not that the create screen has the labels field. **Run the fake Jira:** `node tests/fake-jira/server.mjs --port 4010` (Node 22.18 or newer). It also takes `POST /__fake/{move,rename,comment,child,429}`. Checked: typecheck, eslint, `npm test` (88 files, 945 tests). `next build` was not run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WP-C of
docs/plans/2026-10-29_jira-in-app.md: the project lead's Jira UI. Builds on the WP0 contract (#245, merged).Jira row in the tools panel (
jiraRowOfinfeatures/projects/views.ts, pure and unit-tested):project.sync {system:"jira", on:true}on:false)isIssueKey) + step picker, appends humanproject.linkedwith the URL fromjiraIssueUrlmayAppendProjectrule as the server)Data: the
[company]project page readsgetIntegration+jiraStatusViewon the server and passes the plainJiraStatusView(no secrets) toProjectView. Demo / no database givesnull.Demo: with
nullin the local demo the row shows Jira as on (NUP, hostjira.demo.invalid), andact.project.syncplays the connector: it appends the sameproject.linkedevents (epic, then one per step), no network.Tests:
tests/unit/jira-row.test.ts(new), two new demo e2e tests inprojects.spec.ts(send, link, stop; employee gets no buttons). Typecheck, lint, 855 unit tests, and the full demo suite (30 tests, run on Edge) pass.No dependency, schema,
.github/or engine/admin file touched.