diff --git a/.changeset/6470-gantt-dependency-field-deprecated.md b/.changeset/6470-gantt-dependency-field-deprecated.md
new file mode 100644
index 0000000000..74346edef4
--- /dev/null
+++ b/.changeset/6470-gantt-dependency-field-deprecated.md
@@ -0,0 +1,49 @@
+---
+'@object-ui/types': patch
+'@object-ui/plugin-gantt': patch
+---
+
+`ObjectGanttSchema.dependencyField` is now marked `@deprecated` on both published
+declaration faces, naming `dependenciesField` as the canonical spelling
+(objectui#6470). Nothing is removed and nothing is rejected that was accepted
+before.
+
+**What the two spellings were.** `ObjectGanttSchema` declares both, and
+`getGanttConfig`'s flat branch reads them with a `||`:
+`dependenciesField: schema.dependenciesField || schema.dependencyField`.
+`dependenciesField` is the spec's key (`@objectstack/spec`
+`GanttConfigSchema.dependenciesField`); the singular `dependencyField` has NO
+spec counterpart — zero occurrences across `packages/spec/src`, measured against
+a live positive control on the plural. Until objectui#6051 declared the plural,
+the singular was the ONLY dependencies spelling this interface carried, so for
+the whole time the alias existed the published type taught the non-spec key and
+hid the canonical one.
+
+**What was missing was the ranking, not the behaviour.** The two were declared as
+equals: nothing on either face said which one to author, so a reader — including
+an AI writing metadata, which is the reader this project optimises for — had a
+coin flip between a spec key and pre-spec vocabulary. The marker turns that coin
+flip into a fact the type itself carries, and the zod mirror's description makes
+it readable at runtime as well as in an editor.
+
+This adopts the idiom already ruled for this exact shape rather than inventing a
+second one: `KanbanConfig`'s pre-#2231 aliases (`groupField`, `cardFields`) carry
+`/** @deprecated legacy alias for the spec's X */` plus
+`.describe('Deprecated alias for X')`, and `dependencyField` now reads the same
+way.
+
+**⛔ Not a removal, deliberately.** Deleting the alias — or narrowing the
+renderer's `||` — would break every author who wrote the singular and narrow the
+accept set of a published surface. That is a maintainer decision on a future
+enforce-or-remove card once the deprecation has sat a release, and it is
+explicitly excluded here. Two pins hold the line in both directions:
+`packages/types/src/__tests__/gantt-dependency-field-deprecated-alias.test.ts`
+fails if the marker goes missing AND if the alias stops being declared or
+accepted, and `packages/plugin-gantt/src/ObjectGantt.dependencyAlias.test.tsx`
+fails if the `||` limb is dropped — the two spellings must keep resolving to the
+same config, with the canonical one winning when both carry a value.
+
+`packages/plugin-gantt/README.md`'s `ObjectGanttSchema` example authored the
+singular; it was the only in-repo site that did, and it now authors the plural
+with the alias named as legacy. No runtime code, fixture, example app or catalog
+schema authored it.
diff --git a/packages/plugin-gantt/README.md b/packages/plugin-gantt/README.md
index 6c651d3376..0dd9191aba 100644
--- a/packages/plugin-gantt/README.md
+++ b/packages/plugin-gantt/README.md
@@ -670,10 +670,15 @@ const gantt: ObjectGanttSchema = {
startDateField: 'start_date',
endDateField: 'end_date',
progressField: 'completion_percentage',
- dependencyField: 'dependent_task_ids',
+ dependenciesField: 'dependent_task_ids',
};
```
+`dependenciesField` (plural) is the spec's spelling and the one to author. The
+singular `dependencyField` is a `@deprecated` legacy alias: `ObjectGantt` still
+reads it (`dependenciesField || dependencyField`), so existing metadata keeps
+working, but new metadata should not use it.
+
For a list view served under the `gantt` view type, the same configuration is a
`gantt` block on `ListViewSchema` (typed by `GanttConfig`, also from
`@object-ui/types`) rather than top-level keys — `ObjectGantt` reads either
diff --git a/packages/plugin-gantt/src/ObjectGantt.dependencyAlias.test.tsx b/packages/plugin-gantt/src/ObjectGantt.dependencyAlias.test.tsx
new file mode 100644
index 0000000000..fd9ab6cccf
--- /dev/null
+++ b/packages/plugin-gantt/src/ObjectGantt.dependencyAlias.test.tsx
@@ -0,0 +1,176 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * Behaviour pin — deprecating `dependencyField` changed NOTHING at the read site
+ * (objectui#6470).
+ *
+ * ## What this file is for
+ *
+ * objectui#6470 marks the singular `dependencyField` `@deprecated` on both
+ * published declaration faces (`packages/types/src/objectql.ts` and its zod
+ * mirror), naming the spec's `dependenciesField` as canonical. The marker is
+ * documentation; `getGanttConfig`'s flat branch still reads
+ * `dependenciesField || dependencyField` and every author who wrote the singular
+ * must keep rendering exactly as before.
+ *
+ * ⛔ Removal was explicitly excluded from that card and deferred to a future
+ * enforce-or-remove decision. This suite is what makes the exclusion mechanical:
+ * drop the `|| schema.dependencyField` limb and the first two cases below go red
+ * naming the break, rather than a published surface silently losing an accepted
+ * spelling.
+ *
+ * ## Why the two spellings name DIFFERENT record fields here
+ *
+ * A fixture where both spellings point at the same field can only prove that
+ * SOMETHING resolved; it cannot show WHICH limb of the `||` produced it, so it
+ * would stay green with either limb deleted. Pointing them at different fields
+ * (`legacy_preds` vs `canonical_preds`, with values that cannot be confused)
+ * makes the resolved config visible in the rendered tasks — the same technique
+ * `ObjectGantt.blockPrecedence.test.tsx` uses for the block/flat flip.
+ *
+ * The "same config" half the card asks for is then pinned directly, by the
+ * third case: the two spellings aimed at ONE field produce byte-identical task
+ * dependencies.
+ *
+ * ## The observable
+ *
+ * `dependenciesField` is not passed to `GanttView` as a config value — it is
+ * consumed on the way in (`dependencies: normalizeDependencies(record[dep])`)
+ * and again as the `autoSchedule` / `onDependencyCreate` switches. Both are
+ * observed: the per-task dependency ids say WHICH field was read, and
+ * `autoSchedule` says the config resolved to a truthy field name at all.
+ */
+import React from 'react';
+import { render, waitFor } from '@testing-library/react';
+import { describe, it, expect, vi } from 'vitest';
+import { ObjectGantt } from './ObjectGantt';
+
+vi.mock('./GanttView', () => ({
+ GanttView: ({ tasks, autoSchedule }: any) => (
+
t.dependencies.join('+')).join('|')}
+ data-autoschedule={String(!!autoSchedule)}
+ />
+ ),
+}));
+
+vi.mock('./ResourceWorkload', () => ({
+ ResourceWorkload: ({ tasks }: any) =>
,
+}));
+
+/**
+ * One record carrying BOTH candidate fields, with values that cannot be
+ * confused: the legacy field's ids start `L`, the canonical field's start `C`.
+ */
+const INLINE = [
+ {
+ id: '1',
+ name: 'Alpha',
+ start: '2024-01-01',
+ end: '2024-01-05',
+ legacy_preds: 'L1,L2',
+ canonical_preds: 'C1,C2',
+ shared_preds: 'S1,S2',
+ },
+];
+
+const DATES = { startDateField: 'start', endDateField: 'end', titleField: 'name' };
+
+async function rendered(extra: Record
, objectName: string) {
+ const { container } = render(
+ ,
+ );
+ const el = () => container.querySelector('[data-testid="gantt-view"]') as HTMLElement;
+ await waitFor(() => expect(el()?.getAttribute('data-count')).toBe('1'));
+ return {
+ deps: el().getAttribute('data-deps'),
+ autoSchedule: el().getAttribute('data-autoschedule'),
+ };
+}
+
+describe('both dependency spellings still resolve to the same config (objectui#6470)', () => {
+ it('the DEPRECATED singular is still read', async () => {
+ // ⛔ The removal guard. Deleting `|| schema.dependencyField` from
+ // `getGanttConfig` turns this red — every author who wrote the singular
+ // loses their dependency links, and `autoSchedule` silently switches off
+ // with them.
+ const { deps, autoSchedule } = await rendered(
+ { dependencyField: 'legacy_preds' },
+ 'alias_singular',
+ );
+ expect(deps).toBe('L1+L2');
+ expect(autoSchedule).toBe('true');
+ });
+
+ it('the CANONICAL plural is read', async () => {
+ // The counter-probe: the assertion above must be about the singular limb,
+ // not about dependencies working at all.
+ const { deps, autoSchedule } = await rendered(
+ { dependenciesField: 'canonical_preds' },
+ 'alias_plural',
+ );
+ expect(deps).toBe('C1+C2');
+ expect(autoSchedule).toBe('true');
+ });
+
+ it('aimed at ONE field, the two spellings produce an identical result', async () => {
+ // The card's acceptance criterion, stated directly: the deprecation is a
+ // marker, so the singular and the plural are interchangeable at the read
+ // site. Compared value-to-value rather than each against a literal, so this
+ // stays true however `normalizeDependencies` evolves.
+ const viaLegacy = await rendered({ dependencyField: 'shared_preds' }, 'alias_same_a');
+ const viaCanonical = await rendered({ dependenciesField: 'shared_preds' }, 'alias_same_b');
+ expect(viaLegacy).toEqual(viaCanonical);
+ expect(viaLegacy.deps).toBe('S1+S2');
+ });
+
+ it('the canonical spelling WINS when a node carries both', async () => {
+ // Precedence, and the reason the deprecation marker is truthful: an author
+ // migrating key-by-key gets the canonical value, not a merge and not the
+ // legacy one.
+ const { deps } = await rendered(
+ { dependenciesField: 'canonical_preds', dependencyField: 'legacy_preds' },
+ 'alias_both',
+ );
+ expect(deps).toBe('C1+C2');
+ });
+
+ it('an EMPTY canonical value falls through to the alias — a property of `||`', async () => {
+ // Recorded, not designed. `dependenciesField: ''` is falsy, so the `||`
+ // reaches the singular; `??` would not. Pinned so that a future rewrite of
+ // that expression is a decision rather than an accident — and so the
+ // enforce-or-remove card inherits the real accept semantics instead of
+ // re-deriving them.
+ const { deps } = await rendered(
+ { dependenciesField: '', dependencyField: 'legacy_preds' },
+ 'alias_empty_canonical',
+ );
+ expect(deps).toBe('L1+L2');
+ });
+
+ it('neither spelling authored: no dependencies, and auto-scheduling stays OFF', async () => {
+ // Non-vacuity for every case above — without this, a harness that always
+ // reported the same string would pass them all.
+ const { deps, autoSchedule } = await rendered({}, 'alias_none');
+ expect(deps).toBe('');
+ expect(autoSchedule).toBe('false');
+ });
+});
diff --git a/packages/types/src/__tests__/gantt-dependency-field-deprecated-alias.test.ts b/packages/types/src/__tests__/gantt-dependency-field-deprecated-alias.test.ts
new file mode 100644
index 0000000000..865ad3df40
--- /dev/null
+++ b/packages/types/src/__tests__/gantt-dependency-field-deprecated-alias.test.ts
@@ -0,0 +1,226 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * Deprecation pin — `ObjectGanttSchema.dependencyField` is a DECLARED legacy
+ * alias, and both published faces now say so (objectui#6470).
+ *
+ * ## What was wrong, and what it was NOT
+ *
+ * `ObjectGanttSchema` declares two spellings of one thing. `dependenciesField`
+ * is the spec's (`@objectstack/spec` `GanttConfigSchema.dependenciesField`);
+ * `dependencyField`, the singular, has no spec counterpart at all — zero
+ * occurrences across `packages/spec/src`, against a live positive control on the
+ * plural. Until objectui#6051 declared the plural, the singular was the ONLY
+ * dependencies spelling on this interface, so for the whole time the alias
+ * existed the published type taught the non-spec key and hid the canonical one.
+ *
+ * The card was originally filed as commandment #0.1 — a lenient renderer
+ * fallback to an UNDECLARED key. Triage's dissent measured that and it is not
+ * this shape: the singular is declared outright, documented, and deliberately
+ * retained, with the rationale written next to the canonical key. The honest
+ * description is a types-vs-spec divergence, and the remedy re-scoped to match
+ * (triage ruling 2026-08-26): mark it, keep it.
+ *
+ * ## Why the marker is the whole deliverable, and removal is not
+ *
+ * Two spellings declared as EQUALS is what a reader could not resolve. Nothing
+ * on either face said which one to write, so an author — or an AI writing
+ * metadata, which is the reader this project optimises for — had a coin flip
+ * between a spec key and a pre-spec alias. A machine-readable marker turns the
+ * coin flip into a fact the type itself carries.
+ *
+ * ⛔ Deleting the alias is deliberately NOT in this card. It would narrow the
+ * accept set of a published surface — a breaking removal of a published
+ * capability, which is a maintainer call on a future enforce-or-remove card once
+ * the deprecation has sat a release. That is why the pins below assert the alias
+ * is still THERE and still ACCEPTED as loudly as they assert it is tagged.
+ *
+ * ## The two failure modes, which are opposite
+ *
+ * 1. the tag is missing or nameless — the deprecation is cosmetic, no reader
+ * learns which spelling is canonical, and the card bought nothing;
+ * 2. the alias is gone — the deprecation quietly became the removal that was
+ * explicitly excluded, breaking every author who wrote the singular.
+ *
+ * A test asserting only (1) passes on a tree where the alias was deleted; one
+ * asserting only (2) passes on a tree where the tag never shipped. Both halves
+ * are pinned.
+ *
+ * ## Why the TS face is read as SOURCE TEXT and the zod face at RUNTIME
+ *
+ * They carry the statement in different places, and each is checked where it
+ * actually lives. A TS `@deprecated` exists only in the JSDoc — it reaches
+ * consumers through `dist/*.d.ts` and their editor, and there is no runtime
+ * value to interrogate, so the source is read (the idiom
+ * `plugin-component-input-deprecation.test.ts` established, for the same reason
+ * and with the same anchoring discipline). The zod mirror's `.describe()` IS a
+ * runtime string on the parsed schema, so it is read off the schema object —
+ * which is what makes the mirror half of this deprecation machine-readable
+ * rather than merely human-readable.
+ */
+
+import { readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { describe, it, expect } from 'vitest';
+import { ObjectGanttSchema } from '../zod/objectql.zod.js';
+import type { ObjectGanttSchema as ObjectGanttSchemaTS } from '../objectql.js';
+
+const OBJECTQL_SRC = readFileSync(
+ fileURLToPath(new URL('../objectql.ts', import.meta.url)),
+ 'utf8',
+);
+
+/** The alias member, exactly as `objectql.ts` spells it. */
+const ALIAS = 'dependencyField?: string;';
+/** The canonical member, exactly as `objectql.ts` spells it. */
+const CANONICAL = "dependenciesField?: GanttConfig['dependenciesField'];";
+
+/**
+ * The JSDoc block immediately preceding a member, or `null` when the member is
+ * not preceded by one.
+ *
+ * Anchored to the member rather than searching the file for `@deprecated`:
+ * `objectql.ts` carries fifteen unrelated deprecations, so a file-wide search
+ * would go green on any of them and say nothing about this key.
+ */
+function docBlockBefore(member: string): string | null {
+ const at = OBJECTQL_SRC.indexOf(member);
+ if (at === -1) return null;
+ const before = OBJECTQL_SRC.slice(0, at);
+ const close = before.lastIndexOf('*/');
+ // Only whitespace may sit between the block and the member, otherwise the
+ // block belongs to some earlier member and says nothing about this one.
+ if (close === -1 || before.slice(close + 2).trim() !== '') return null;
+ const open = before.lastIndexOf('/**', close);
+ if (open === -1) return null;
+ return before.slice(open, close + 2);
+}
+
+describe('ObjectGanttSchema.dependencyField — the TS face declares the deprecation', () => {
+ it('the member is unique in the file, so the anchor cannot drift onto a twin', () => {
+ // Non-vacuity for every `docBlockBefore(ALIAS)` below: `indexOf` takes the
+ // FIRST hit, so a second declaration of the same member would let this suite
+ // report on the wrong one.
+ expect(OBJECTQL_SRC.split(ALIAS)).toHaveLength(2);
+ });
+
+ it('is still declared — deprecating is not deleting', () => {
+ expect(OBJECTQL_SRC).toContain(ALIAS);
+ });
+
+ it('carries a JSDoc block attached to the member itself', () => {
+ expect(docBlockBefore(ALIAS)).not.toBeNull();
+ });
+
+ it('tags that block `@deprecated`', () => {
+ expect(docBlockBefore(ALIAS)).toContain('@deprecated');
+ });
+
+ it('names the canonical spelling, so the marker is actionable', () => {
+ // A bare `@deprecated` tells an author to stop, not what to write instead —
+ // and "what to write instead" is the entire content of this card.
+ expect(docBlockBefore(ALIAS)).toContain('dependenciesField');
+ });
+
+ it('says the alias is still READ, so nobody reads the tag as a removal notice', () => {
+ expect(docBlockBefore(ALIAS)).toContain('dependenciesField || dependencyField');
+ });
+
+ it('does NOT deprecate the canonical key — the control', () => {
+ // The tag must be attached to ONE member. If a future edit widened the
+ // block, or moved it, the spec's own key would read as deprecated and this
+ // card would have inverted its own result.
+ const canonicalDoc = docBlockBefore(CANONICAL);
+ expect(canonicalDoc).not.toBeNull();
+ expect(canonicalDoc).not.toContain('@deprecated ');
+ expect(canonicalDoc).toContain('CANONICAL');
+ });
+});
+
+describe('ObjectGanttSchema.dependencyField — the zod mirror declares it too', () => {
+ it('is still declared on the mirror', () => {
+ expect(Object.keys(ObjectGanttSchema.shape)).toContain('dependencyField');
+ });
+
+ it('its description states the deprecation and names the replacement', () => {
+ // Runtime-readable, which is the half a JSDoc cannot give: this string is on
+ // the schema object, so a gate or a generated form can ask.
+ const description = ObjectGanttSchema.shape.dependencyField.description;
+ expect(description).toBeTruthy();
+ expect(description).toMatch(/deprecated/i);
+ expect(description).toContain('dependenciesField');
+ });
+
+ it('follows the ruled idiom rather than a third spelling of "deprecated"', () => {
+ // `KanbanConfig`'s pre-#2231 aliases are the shape this adopts. If that
+ // convention is ever restated differently, this pin makes the divergence a
+ // decision instead of a drift.
+ expect(ObjectGanttSchema.shape.dependencyField.description).toMatch(
+ /^Deprecated alias for /,
+ );
+ });
+
+ it('the canonical key is NOT described as deprecated — the control', () => {
+ const canonical = ObjectGanttSchema.shape.dependenciesField.description;
+ expect(canonical ?? '').not.toMatch(/deprecated/i);
+ });
+});
+
+describe('ObjectGanttSchema.dependencyField — still ACCEPTED (removal was excluded)', () => {
+ const MINIMAL = {
+ type: 'object-gantt',
+ objectName: 'task',
+ startDateField: 'start',
+ endDateField: 'end',
+ } as const;
+
+ it('parses green when the singular is authored alone', () => {
+ const result = ObjectGanttSchema.safeParse({ ...MINIMAL, dependencyField: 'preds' });
+ expect(result.success ? null : result.error.issues).toBe(null);
+ });
+
+ it('parses green when both spellings are authored together', () => {
+ const result = ObjectGanttSchema.safeParse({
+ ...MINIMAL,
+ dependencyField: 'preds',
+ dependenciesField: 'predecessors',
+ });
+ expect(result.success ? null : result.error.issues).toBe(null);
+ });
+
+ it('still TYPE-validates the alias — a deprecated key is not an unchecked one', () => {
+ // Counter-probe for the two greens above: it must be the KEY that is
+ // accepted, not everything. `dependencyField: 5` was refused before this
+ // card and is refused after it.
+ const result = ObjectGanttSchema.safeParse({ ...MINIMAL, dependencyField: 5 });
+ expect(result.success).toBe(false);
+ if (result.success) return;
+ expect(result.error.issues.find((i) => i.path[0] === 'dependencyField')).toBeTruthy();
+ });
+});
+
+describe('ObjectGanttSchema.dependencyField — the TS member is unchanged', () => {
+ it('is still declared `string | undefined`', () => {
+ // Real enforcement: `tsconfig.test.json` compiles this file. If the member
+ // were deleted, it would resolve to `any` through `BaseSchema`'s
+ // `[key: string]: any` index signature, the assignment below would start
+ // succeeding, and the directive would fail the build as an unused
+ // `@ts-expect-error` (TS2578) — NAMING the removal. That is the pin against
+ // the deprecation quietly becoming the excluded removal.
+
+ // @ts-expect-error — `dependencyField` is declared `string | undefined`.
+ const bad: ObjectGanttSchemaTS['dependencyField'] = 5;
+
+ // Counter-probe: without this, a declaration narrowed to `never` would
+ // satisfy the directive above while breaking every existing author.
+ const good: ObjectGanttSchemaTS['dependencyField'] = 'dependent_task_ids';
+
+ expect([bad, good]).toHaveLength(2);
+ });
+});
diff --git a/packages/types/src/objectql.ts b/packages/types/src/objectql.ts
index 0c39c3eef6..7ab4ca9363 100644
--- a/packages/types/src/objectql.ts
+++ b/packages/types/src/objectql.ts
@@ -2285,7 +2285,25 @@ export interface ObjectGanttSchema extends BaseSchema {
endDateField?: string;
/** Field for task title/name */
titleField?: string;
- /** Field for task dependencies */
+ /**
+ * Field for task dependencies.
+ *
+ * @deprecated Legacy alias — author {@link ObjectGanttSchema.dependenciesField}
+ * instead. The plural is the spec's spelling (`@objectstack/spec`
+ * `GanttConfigSchema.dependenciesField`); this singular has NO spec
+ * counterpart. It is pre-spec objectui vocabulary, and until objectui#6051 it
+ * was the ONLY dependencies spelling this interface declared — so for the whole
+ * time the alias existed the published type taught the non-spec key and hid the
+ * canonical one. That is what this tag exists to correct: the two were declared
+ * as equals, and a reader had no way to learn which one to write.
+ *
+ * Still accepted, deliberately. `getGanttConfig`'s flat branch reads
+ * `dependenciesField || dependencyField`, so metadata already written against
+ * this key renders unchanged and the canonical key wins wherever both carry a
+ * value. Deprecating is NOT removing: dropping the alias would narrow the
+ * accept set of a published surface, which is a separate enforce-or-remove
+ * decision (objectui#6470) and not something this marker takes.
+ */
dependencyField?: string;
/** Field for progress (0-100) */
progressField?: string;
@@ -2440,7 +2458,8 @@ export interface ObjectGanttSchema extends BaseSchema {
* Record field holding this task's predecessors. The CANONICAL spelling — the
* flat branch reads `dependenciesField || dependencyField`, so the singular
* {@link ObjectGanttSchema.dependencyField} above stays accepted as the legacy
- * alias and this one wins.
+ * alias and this one wins. That alias is `@deprecated` (objectui#6470): still
+ * read, no longer taught.
*/
dependenciesField?: GanttConfig['dependenciesField'];
/**
diff --git a/packages/types/src/zod/objectql.zod.ts b/packages/types/src/zod/objectql.zod.ts
index a69a6db28a..c683993f19 100644
--- a/packages/types/src/zod/objectql.zod.ts
+++ b/packages/types/src/zod/objectql.zod.ts
@@ -666,7 +666,14 @@ export const ObjectGanttSchema = BaseSchema.extend({
startDateField: z.string().optional().describe('Start date field'),
endDateField: z.string().optional().describe('End date field'),
titleField: z.string().optional().describe('Title field'),
- dependencyField: z.string().optional().describe('Dependency field'),
+ // The legacy singular alias. Kept accepted — `getGanttConfig`'s flat branch
+ // reads `dependenciesField || dependencyField` — but no longer declared as an
+ // equal of the canonical key: same treatment `KanbanConfig` above gives
+ // `groupField`/`cardFields`, so this adopts the ruled idiom rather than a
+ // second spelling of "deprecated". The canonical `dependenciesField` is
+ // declared below, BY REFERENCE to the spec (objectui#6470).
+ /** @deprecated legacy alias for the spec's `dependenciesField` */
+ dependencyField: z.string().optional().describe('Deprecated alias for dependenciesField'),
progressField: z.string().optional().describe('Progress field'),
// DERIVED from the spec's `GanttConfigSchema.shape.viewMode` (an optional
// enum, deliberately WITHOUT a default) so the member list cannot drift