From abe0cb96259530eeebbe0e9625668a3ada8a4294 Mon Sep 17 00:00:00 2001 From: Michael Uray <25169478+MichaelUray@users.noreply.github.com> Date: Thu, 9 Jul 2026 04:08:17 +0200 Subject: [PATCH] feat(tracker): Gantt scheduling schema (startDate + IssueRelation) + version bump (#10851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(tracker): add Gantt scheduling schema (startDate + IssueRelation) Schema-only foundation for the upcoming Gantt-chart view in tracker. No UI in this PR. Changes: - Issue.startDate: Timestamp | null (interface + IssueDraft + @Prop with @Index) - Milestone.startDate: Timestamp | null (interface + @Prop, reusing the existing tracker.string.StartDate IntlString) - New DependencyKind type ('finish-to-start' | 'start-to-start' | 'finish-to-finish' | 'start-to-finish') - New IssueRelation AttachedDoc class with kind: DependencyKind, signed lag: number — registered in models/tracker via TIssueRelation - 7 new IntlString keys: IssueStartDate, GanttDependency, GanttDependency{FinishToStart,StartToStart,FinishToFinish,StartToFinish}, GanttLag — all 13 locales updated - Cross-plugin literal updates in importer + github sync to satisfy the new required Issue.startDate / Milestone.startDate fields: - packages/importer/src/importer/importer.ts: AttachedData literal - services/github/pod-github/src/sync/issueBase.ts: 'startDate' added to GithubIssueData Omit list (github sync does not own scheduling) - services/github/pod-github/src/sync/issues.ts + pullrequests.ts: AttachedData literals Out of scope (deferred to follow-up PRs): - UI for Gantt view, drag/resize, dependency editor, critical path - blockedBy → IssueRelation migration (ships atomically with the writer redirect in the dependency-UI PR) - LinkIssues permission (tracker uses forbid-style permissions; needs maintainer discussion) - Activity-feed wiring for IssueRelation (needs a producer to test against) - IssueTemplate.startDate (template propagation semantics undecided) Signed-off-by: Michael Uray * test(model-tracker): add migrateAddStartDate jest tests 3 tests covering migrateAddStartDate: - writes startDate=null to Issues in DOMAIN_TASK with the right filter - writes startDate=null to Milestones in DOMAIN_TRACKER with the right filter - issues exactly two update calls (one per class) Follows the MigrationClient mock pattern from models/chat/src/__tests__/migration.test.ts. Signed-off-by: Michael Uray * feat(model-tracker): add migrateAddStartDate + wire into trackerOperation Backfills startDate=null on existing Issues (DOMAIN_TASK) and Milestones (DOMAIN_TRACKER) so the new schema field has a defined value on every pre-existing document. Idempotent via the standard tryMigrate state-key mechanism (state: 'gantt-add-startdate'). Verified domain choices against existing migration helpers: - migrateIdentifiers / passIdentifierToParentInfo use DOMAIN_TASK for Issues (lines 145, 161 in this file). - TMilestone @Model decorator confirms DOMAIN_TRACKER for Milestones (models/tracker/src/types.ts:372). Signed-off-by: Michael Uray * feat(tracker): expose Issue.startDate / Milestone.startDate in UI; tighten typing UI changes (so the new schema fields are actually editable, in chronological order Start → Due/Target): - New StartDateEditor.svelte (mirrors DueDateEditor.svelte for startDate) - ControlPanel: render Start Date row above Due Date row in the issue side panel; both always-visible (no `!== null` guard) so users can set them on issues that don't have a date yet - NewMilestone form: Start Date input above Target Date input - Milestone list view: Start Date column before Target Date column - TIssueRelation: tighten interface to `extends AttachedDoc` so attachedTo + collection are statically typed. The model class re-declares `collection: 'relations'` to match the narrower base. - Drop 4 unused Dependency-kind IntlString keys (FinishToFinish, FinishToStart, StartToFinish, StartToStart) — they had no consumer in PR 1; will be re-introduced in PR 4 (dependency editor). - Simplify migration.ts comments — drop ageing line-references. Signed-off-by: Michael Uray * fix(tracker): set explicit @Prop ranks for Milestone date fields The DocAttributeBar side panel sorts attributes by attr.rank ?? toRank(_id) (see plugins/view-resources/src/components/ClassAttributeBar.svelte:42-47), so without explicit ranks the visible order on a Milestone was hash-based (startDate before Status, breaking the chronological flow the user expects). Set ranks so the side panel renders Status → Start date → Target date. Comments and attachments stay where they are (they're collections, filtered out of the attribute panel by categorizeFields). Issues are unaffected — the Issue side panel is the custom ControlPanel.svelte which renders Start date / Due date in explicit slots (see PR 1's UI commit). Signed-off-by: Michael Uray * fix(tracker-resources): EditMilestone renders Status/Start/Target in body in chronological order The right-side DocAttributeBar sorts attributes by attr.rank ?? toRank(_id), giving startDate before status (toRank('startDate') < toRank('status') lexicographically). Setting an explicit rank via @Prop's third arg did not propagate through the workspace upgrade for existing Attribute documents in the model TX log — the rank made it into the bundled txes but the existing Attribute creation TXes are not replaced on upgrade-workspace. Pivot: render Status, Start date, Target date in the EditMilestone body in explicit chronological order, and add 'status', 'startDate', 'targetDate' to ignoreKeys so they don't appear duplicated in the side panel. This mirrors how Issue's ControlPanel.svelte handles its date fields. Reverts the no-op @Prop rank attempt. Signed-off-by: Michael Uray * fix(fulltext): bump model version to 0.7.423 to match deployed workspaces The fulltext-pod's compiled model version (baked into bundle/model.json via common/scripts/version.txt at build time) lags whenever the workspaces have been migrated to a newer patch but the pod was not rebuilt. In that state the indexer rejects every incoming Tx with a `wrong version` warning, new issues silently fail to land in Elasticsearch, and search returns empty results for any document created after the migration. Bumping `version.txt` aligns the compiled model with the workspaces. All future builds (front, transactor, workspace, tool, fulltext) will emit 0.7.423, the indexer accepts the Tx stream again, and the deferred backlog gets consumed automatically — no manual reindex needed. This commit is the build-side companion to the schema migration in this same PR. Without it the fulltext-pod cannot consume the migrated workspace's Tx events. Signed-off-by: Michael Uray * chore: apply rush format after develop merge Resolves the failing formatting check requested by @ArtyomSavchenko in review of #10851 after the develop branch merge. Affects three files in our PR scope: - models/tracker/src/migration.ts: collapse short multi-line client.update call - plugins/tracker/src/index.ts: inline DependencyKind union + IssueRelation comment - plugins/tracker-resources/src/components/milestones/EditMilestone.svelte: reformat inline arrow handlers, move QueryIssuesList block ahead of diff --git a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte index 6139262bc3..a74c93fa50 100644 --- a/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte +++ b/plugins/tracker-resources/src/components/milestones/NewMilestone.svelte @@ -34,6 +34,7 @@ status: MilestoneStatus.Planned, comments: 0, attachments: 0, + startDate: null, targetDate: Date.now() + 14 * 24 * 60 * 60 * 1000 } @@ -76,6 +77,14 @@ /> + milestone?: Ref | null @@ -236,6 +251,7 @@ export interface IssueDraft { assignee: Ref | null component: Ref | null space: Ref + startDate: Timestamp | null dueDate: Timestamp | null milestone?: Ref | null @@ -323,6 +339,21 @@ export interface IssueParentInfo { space: Ref } +/** + * Typed dependency between two Issues, used by the Gantt view to compute + * cascade scheduling and critical path. + * + * Persisted as an AttachedDoc collection 'relations' on the source Issue. + * + * @public + */ +export interface IssueRelation extends AttachedDoc { + target: Ref // successor + kind: DependencyKind + /** Lag in schedule days; can be negative (overlap). */ + lag: number +} + /** * @public */ @@ -366,6 +397,7 @@ const pluginState = plugin(trackerId, { class: { Project: '' as Ref>, Issue: '' as Ref>, + IssueRelation: '' as Ref>, IssueTemplate: '' as Ref>, Component: '' as Ref>, IssueStatus: '' as Ref>, @@ -520,6 +552,9 @@ const pluginState = plugin(trackerId, { Project: '' as IntlString, RelatedIssues: '' as IntlString, Issue: '' as IntlString, + IssueStartDate: '' as IntlString, + GanttDependency: '' as IntlString, + GanttLag: '' as IntlString, NewProject: '' as IntlString, UnsetParentIssue: '' as IntlString, ForbidCreateProjectPermission: '' as IntlString, diff --git a/services/github/pod-github/src/sync/issueBase.ts b/services/github/pod-github/src/sync/issueBase.ts index bf4ce7535f..ff181599b6 100644 --- a/services/github/pod-github/src/sync/issueBase.ts +++ b/services/github/pod-github/src/sync/issueBase.ts @@ -69,6 +69,7 @@ WithMarkup, | 'reports' | 'childInfo' | 'dueDate' +| 'startDate' | 'kind' | 'reviews' | 'reviewThreads' diff --git a/services/github/pod-github/src/sync/issues.ts b/services/github/pod-github/src/sync/issues.ts index d4121953a8..07d7b2cea9 100644 --- a/services/github/pod-github/src/sync/issues.ts +++ b/services/github/pod-github/src/sync/issues.ts @@ -868,6 +868,7 @@ export class IssueSyncManager extends IssueSyncManagerBase implements DocSyncMan rank: calcRank(lastOne, undefined), comments: 0, subIssues: 0, + startDate: null, dueDate: null, parents: [], reportedTime: 0, diff --git a/services/github/pod-github/src/sync/pullrequests.ts b/services/github/pod-github/src/sync/pullrequests.ts index 0d57422205..ebc6e9054d 100644 --- a/services/github/pod-github/src/sync/pullrequests.ts +++ b/services/github/pod-github/src/sync/pullrequests.ts @@ -1169,6 +1169,7 @@ export class PullRequestSyncManager extends IssueSyncManagerBase implements DocS rank: calcRank(lastOne, undefined), comments: 0, subIssues: 0, + startDate: null, dueDate: null, parents: [], reportedTime: 0, diff --git a/tests/sanity/tests/model/tracker/issues-details-page.ts b/tests/sanity/tests/model/tracker/issues-details-page.ts index 53fb1f555f..4e2f1ca7ae 100644 --- a/tests/sanity/tests/model/tracker/issues-details-page.ts +++ b/tests/sanity/tests/model/tracker/issues-details-page.ts @@ -29,7 +29,11 @@ export class IssuesDetailsPage extends CommonTrackerPage { readonly textEstimation = (): Locator => this.page.locator('//span[text()="Estimation"]/following-sibling::div[1]/button/span') - readonly buttonEstimation = (): Locator => this.page.locator('(//span[text()="Estimation"]/../div/button)[3]') + // ControlPanel now renders Start Date + Due Date rows unconditionally + // (Gantt schema PR — Issue.startDate). Both editors emit a `