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<Issue> 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<Issue|GithubPullRequest> 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 <michaeluray@users.noreply.github.com>
This commit is contained in:
Michael Uray
2026-05-18 19:37:45 +00:00
parent 18ef71bc04
commit 0e6d0dfc7f
24 changed files with 176 additions and 0 deletions
+44
View File
@@ -119,6 +119,22 @@ export enum IssuePriority {
Low
}
/**
* Dependency kind between two Issues for Gantt scheduling.
*
* - `finish-to-start` (FS): A must finish before B can start. Most common.
* - `start-to-start` (SS): A must start before B can start.
* - `finish-to-finish` (FF): A must finish before B can finish.
* - `start-to-finish` (SF): A must start before B can finish. Rare.
*
* @public
*/
export type DependencyKind =
| 'finish-to-start'
| 'start-to-start'
| 'finish-to-finish'
| 'start-to-finish'
/**
* @public
*/
@@ -175,6 +191,7 @@ export interface Milestone extends Doc {
comments: number
attachments?: number
startDate: Timestamp | null // null = open-ended begin marker
targetDate: Timestamp
}
@@ -196,6 +213,8 @@ export interface Issue extends Task {
relations?: RelatedDocument[]
parents: IssueParentInfo[]
startDate: Timestamp | null // for Gantt scheduling; null = unscheduled
space: Ref<Project>
milestone?: Ref<Milestone> | null
@@ -236,6 +255,7 @@ export interface IssueDraft {
assignee: Ref<Person> | null
component: Ref<Component> | null
space: Ref<Project>
startDate: Timestamp | null
dueDate: Timestamp | null
milestone?: Ref<Milestone> | null
@@ -323,6 +343,22 @@ export interface IssueParentInfo {
space: Ref<Space>
}
/**
* 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 {
attachedTo: Ref<Issue> // source / predecessor
target: Ref<Issue> // successor
kind: DependencyKind
/** Lag in schedule days; can be negative (overlap). */
lag: number
}
/**
* @public
*/
@@ -366,6 +402,7 @@ const pluginState = plugin(trackerId, {
class: {
Project: '' as Ref<Class<Project>>,
Issue: '' as Ref<Class<Issue>>,
IssueRelation: '' as Ref<Class<IssueRelation>>,
IssueTemplate: '' as Ref<Class<IssueTemplate>>,
Component: '' as Ref<Class<Component>>,
IssueStatus: '' as Ref<Class<IssueStatus>>,
@@ -520,6 +557,13 @@ const pluginState = plugin(trackerId, {
Project: '' as IntlString,
RelatedIssues: '' as IntlString,
Issue: '' as IntlString,
IssueStartDate: '' as IntlString,
GanttDependency: '' as IntlString,
GanttDependencyFinishToFinish: '' as IntlString,
GanttDependencyFinishToStart: '' as IntlString,
GanttDependencyStartToFinish: '' as IntlString,
GanttDependencyStartToStart: '' as IntlString,
GanttLag: '' as IntlString,
NewProject: '' as IntlString,
UnsetParentIssue: '' as IntlString,
ForbidCreateProjectPermission: '' as IntlString,