Skip to content

feat(formation): canonical shared types for formations and checklist templates - #2175

Merged
manishdixitlfx merged 3 commits into
mainfrom
feat/issue-2163
Sep 4, 2026
Merged

feat(formation): canonical shared types for formations and checklist templates#2175
manishdixitlfx merged 3 commits into
mainfrom
feat/issue-2163

Conversation

@manishdixitlfx

@manishdixitlfx manishdixitlfx commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Lands the canonical packages/shared/src/interfaces/formation.interface.ts and a new
packages/shared/src/enums/formation.enum.ts on main, resolving the add/add conflict where
#2033 and #2035 each independently create formation.interface.ts from scratch.

Verified round-trip against #2035's real 17-item seeded template (2 sections, 4 gating items, 4
chat-workspace sub-items) — every field maps with no information loss; #2035 gains two new
required fields on rebase (FormationTemplate.name, FormationTemplateSection.title), which its
flat shape never carried.

Blocks: #2019, #2033, #2035 (and #1993, #1956 later). Full derivation and decisions:
~/lfx/formation-integration-conflict-report.md (2026-09-03 section) and the GH-2163 issue.

Closes #2163

Test plan

  • ./check-headers.sh && yarn lint && yarn format && yarn test && yarn build — all pass
  • New formation.enum.spec.ts: enum exhaustiveness for all three vocabularies (real,
    runtime-enforced) + a minimal template literal round-trips the canonical shape
  • Confirmed the "sub-items nest one level only" invariant is real TS structure but not
    gate-enforced (this package's test is plain vitest run, no type-checking; check-types
    excludes *.spec.ts) — documented in the spec file rather than left as an inert
    @ts-expect-error
  • Merge dry-run (git merge-tree --write-tree) against scratch copies of feat(formation): checklist section and Formations queue, Epic 1 scope (#1958) #2033 and feat(formation): seed the project formation checklist template #2035
    with their local formation.interface.ts/formation.enum.ts deleted (simulating the
    post-merge rebase): clean merge, no add/add conflict, in both cases
  • FORMATION_SUB_STAGE_LABELS: confirmed formation.constants.ts is a genuinely new path on
    main but an existing one on feat(formation): checklist section and Formations queue, Epic 1 scope (#1958) #2033 — so, unlike the interface/enum files, this one cannot
    resolve to a clean automatic merge; feat(formation): checklist section and Formations queue, Epic 1 scope (#1958) #2033's rebase must hand-resolve the add/add by keeping
    this canonical label block and re-adding its own remaining unique constants (severity maps,
    queue tiles, row-action configs) into the same file. Simulated that resolution by hand in a
    scratch worktree and confirmed the merged file references only types already present in the
    canonical formation.interface.ts/formation-checklist.interface.ts/components.interface.ts
    — not full-build-verified there (the disposable worktree has no installed node_modules)
  • Amendments commit: ./check-headers.sh && yarn lint && yarn format && yarn test && yarn build
    — all pass (@lfx-one/shared 60/60 files, lfx-one-ui 92/92 + 83/83 files); grep confirms
    waiting_on_partner, 'activating', 'subproject', and FormationLead are gone from
    packages/shared

🤖 Generated with Claude Code

https://claude.ai/code/session_01CdGb7zS2HyLZtgFAxokw71

@manishdixitlfx
manishdixitlfx requested a review from a team as a code owner September 3, 2026 19:01
Copilot AI balanced review requested due to automatic review settings September 3, 2026 19:01
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Essentials

Run ID: aede414a-3610-4e59-a2ed-cf0b9e11c15e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Shared TypeScript types, constants, and tests only; no auth, persistence, or production behavior changes until downstream PRs consume them.

Overview
Introduces canonical Epic 1 formation domain types in @lfx-one/shared so parallel formation work (#2019, #2033, #2035) can share one contract instead of conflicting formation.interface.ts add/add merges.

Adds formation.interface.ts with queue/checklist shapes (Formation, FormationItem, activity, nested FormationTemplate with optional one-level sub_items), API response types, and deliberate product/architecture choices: FormationSubStage is only exploratory | engaged | on_hold (readiness is is_activating, not a stage); item statuses include blocked and awaiting_acceptance (only done counts toward gates); FormationUser for plain user refs; child_project entity type; template owner_team uses enums while runtime items stay string | null until #1957.

Adds formation.enum.ts (FormationTemplateSectionKey, seven-team FormationOwnerTeam without PMO, five-value FormationActionType including status_only) and FORMATION_SUB_STAGE_LABELS in formation.constants.ts (distinct from the separate ProjectStage formation label map). formation.enum.spec.ts locks enum vocabularies and the three sub-stage label keys.

Barrel exports wire the new modules through interfaces, enums, and constants indexes. No runtime services, fixtures, or UI in this PR.

Reviewed by Cursor Bugbot for commit 288e87f. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces canonical shared formation contracts to unblock parallel formation feature branches.

Changes:

  • Adds formation interfaces and response shapes.
  • Adds section, owner-team, and action enums.
  • Adds enum vocabulary and template-shape tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shared/src/interfaces/index.ts Exports formation interfaces.
packages/shared/src/interfaces/formation.interface.ts Defines formation domain contracts.
packages/shared/src/enums/index.ts Exports formation enums.
packages/shared/src/enums/formation.enum.ts Defines canonical formation vocabularies.
packages/shared/src/enums/formation.enum.spec.ts Tests enum values and a template fixture.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

],
},
],
} satisfies FormationTemplate;
entity_type: FormationEntityType;
template_uid: string;
template_version: number;
sub_stage: FormationSubStage;

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (1)

packages/shared/src/interfaces/formation.interface.ts:50

  • Linked issue #1957 defines Activating as all gating items being done and announcement_date being set. This definition omits the date and says the downstream helper is identical, but #2033's helper only inspects items and also treats skipped as resolved. That would give fixtures and the eventual service different meanings for is_activating; define the canonical semantics here and align the helper when #2033 rebases.
  /** Derived: every gating item `done` (and at least one gating item exists). TODO(#1957): backend-derived once real; see `deriveFormationReadinessSummary`, which computes this identically. */

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

packages/shared/src/interfaces/formation.interface.ts:30

  • FormationSubStage is also the type of Formation.sub_stage, so restricting it to queue-filter values makes FormationChecklistResponse unable to represent a Confidential formation. Issue #1957 now explicitly requires Confidential projects to receive a checklist like every other Formation stage. Split the full runtime stage union from the narrower queue-filter union (and use the latter for tiles/labels) so the canonical service payload can include confidential without adding it as a staff queue pill.
export type FormationSubStage = 'exploratory' | 'engaged' | 'on_hold';

packages/shared/src/enums/formation.enum.spec.ts:67

  • This satisfies FormationTemplate check is not enforced by either package gate: check-types excludes *.spec.ts, while vitest run transpiles TypeScript without type-checking. Consequently, an incompatible change such as adding a required template field would still leave this “accepts” test green; its only runtime assertion verifies an array length. Move the compile-time fixture into a file included by tsc, or add a dedicated type-test configuration that CI executes.
    } satisfies FormationTemplate;

Comment on lines +57 to +61
/** ISO date. Null until a gating item sets it. */
announcement_date: string | null;
/**
* Derived: every gating item `done` (and at least one gating item exists). An `awaiting_acceptance`
* gating item does not count as `done`, so it keeps this false. TODO(#1957): backend-derived once real.
…templates (GH-2163)

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
…H-2163)

Six amendments to the canonical formation types, per the Epic 1
architecture review and two product calls (4 Sep):

- FormationItemStatus: drop waiting_on_partner (a UI wording of
  blocked, not a stored value), add blocked and awaiting_acceptance
  (an assignee-completed item stays on Pending Actions until the
  formation team accepts it; only done counts toward readiness).
- FormationSubStage: drop activating — it's derived readiness
  (Formation.is_activating), not a PCC sub-stage, and belongs in a
  separate "Gates cleared" badge, never the Stage column.
- can_complete: keep the field, rewrite the comment — there is no
  gate_writer relation; gating is a property of the item, guarded by
  project write permission + is_gating.
- FormationEntityType: rename 'subproject' -> 'child_project'
  (user-facing term is "child project"); documented as a display
  taxonomy to derive later (TODO #1958). FormationQueueTiles.subprojects
  renamed to child_projects to match.
- is_gating doc: "Gates Active" -> "Required for Active", the agreed
  4 Sep vocabulary for the chip/strip/Me-lens marker/email.
- FormationLead -> FormationUser: not an Epic 2 "formation lead"
  record (#1992); it's a plain user reference.

Adds a runtime regression guard on FORMATION_SUB_STAGE_LABELS' key set
in formation.enum.spec.ts.

Every consumer of these types lives on feat/GH-1958-formation-checklist-queue
(#2033) — verified read-only, not touched here. feat/issue-1959 (#2035)
has zero references to any of the six.

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
@dealako

dealako commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@manishdixitlfx the types landing is doing the job #2163 asked for. Naming and the 4 Sep amendments (FormationUser, child_project, awaiting_acceptance, no stored activating) read as deliberate, and the enum specs lock the vocabularies that #2033 and #2035 were about to fork.

I still cannot approve. The branch conflicts with main, and one open Copilot thread on readiness is still right.

Bot reconciliation

Revision tracking

Resolved in d0eb54ba: dropped stored activating and waiting_on_partner, renamed FormationLeadFormationUser and subprojectchild_project, added the FORMATION_SUB_STAGE_LABELS key-set guard.

Still open: the type-test comment (documented, not blocking), the FormationState comment (I would resolve as won't-fix), and the announcement_date / readiness comment (still valid).

CodeRabbit skipped. Cursor Bugbot flagged low risk and did not raise findings.

🔴 Needs changes before approval

@dealako dealako left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canonical shared types for GH-2163 look sound on naming and the 4 Sep amendments. Requesting changes because this branch conflicts with main on the constants barrel, and the announcement_date / is_activating JSDoc still disagrees with #1957 (Copilot's open thread).

  • 🔴 Blocking: 1. Rebase and keep both id-migration.constants and formation.constants exports in packages/shared/src/constants/index.ts.
  • 🟡 Minor: 1. Fix the announcement_date comment (PCC-owned legal field, not "set by a gating item") and say plainly that is_activating is the gates-cleared half of readiness, not the full #1957 predicate.

See the review summary comment for bot reconciliation and revision tracking.

MRashad26
MRashad26 previously approved these changes Sep 4, 2026

@MRashad26 MRashad26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff (7 files, +376/-0, types/constants only) and cross-checked the claimed conflict-resolution/naming decisions against the actual current main.

What this does (GH-2163): Lands the canonical formation.interface.ts + formation.enum.ts on main ahead of three in-flight PRs (#2019/#2033/#2035) that would otherwise each independently create these files and collide (add/add conflicts, plus a genuine duplicate-export name collision on FORMATION_SUB_STAGE_LABELS). Pure types/constants/tests — no runtime consumers touched.

Verified against current code:

  • Confirmed via git grep against origin/main that none of FormationTemplateSection, FormationOwnerTeam, FormationActionType, FormationSubStage, FORMATION_SUB_STAGE_LABELS exist anywhere in packages/shared/src today — this really is a clean net-new addition, not a rename colliding with something already there.
  • The FormationTemplateSectionKey enum rename (from #2035's original FormationTemplateSection) is necessary and correctly applied: FormationTemplateSection is used here as the structural interface name ({ key: FormationTemplateSectionKey; title; items }), so keeping both names distinct avoids the exact collision the PR describes.
  • FORMATION_SUB_STAGE_LABELS's as const satisfies Record<FormationSubStage, string> pattern matches an existing precedent in this package (survey.constants.ts's as const satisfies Record<string, CombinedSurveyStatus>), and the repo's TypeScript version (5.8.3) supports satisfies.
  • formation.enum.spec.ts's exhaustiveness assertions (Object.values(...).sort()) directly pin each enum's member set, and the FormationTemplate "satisfies" object literal round-trips a real nested section→item→sub_item shape — a reasonable, honestly-scoped substitute for the type-level "sub-items nest one level only" invariant, which the spec's own comment explains can't be gate-enforced given this package's vitest run (no type-check) + check-types (excludes *.spec.ts) setup. That's a good call rather than a silently-decorative @ts-expect-error.
  • The doc comments consistently distinguish enum-scoped fields (template-side, curated vocabulary) from plain-string runtime fields (FormationItem.owner_team: string | null, Formation.entity_type) with explicit TODO(#1957)/TODO(#1958) narrowing markers — this is a deliberate, well-reasoned split (template vocab is curated/stable; runtime fields await the real backend service) rather than an inconsistency.
  • FormationItemStatus's awaiting_acceptance addition and the readiness-counting comment ("only done counts toward readiness") are consistent with the Formation.is_activating doc comment's own wording ("An awaiting_acceptance gating item does not count as done").
  • Barrel-file wiring (constants/index.ts, enums/index.ts, interfaces/index.ts) is correctly added; no existing export at those insertion points is disturbed.

Minor, non-blocking observations:

  • This PR proactively documents (in its own description) that it blocks #2019/#2033/#2035 and that #2033's rebase must hand-resolve formation.constants.ts (a same-path add/add it can't auto-merge) — that coordination is called out clearly enough that it's on the follow-up PRs, not a gap in this one.
  • Nothing to flag on test coverage: this is a types-only change, and the new spec file's exhaustiveness + shape-round-trip tests are an appropriate level of verification for what's being added (no runtime logic to exercise).

No issues found. Approving.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Suppressed comments (3)

packages/shared/src/interfaces/formation.interface.ts:30

  • FormationSubStage is also used by Formation.sub_stage, but it cannot represent confidential. Issues #1957 and #1958 require Confidential projects to receive a checklist and appear as ordinary access-filtered queue rows. Define the full service-level sub-stage domain (including confidential) separately from the narrower queue-filter keys; otherwise the canonical response drops a required record or incorrectly adds a Confidential filter.
export type FormationSubStage = 'exploratory' | 'engaged' | 'on_hold';

packages/shared/src/interfaces/formation.interface.ts:58

  • This documents the wrong owner for the field: announcement_date comes from the project record, which the formation service reads but never writes; a gating item does not set it. Correcting this is important because this file is intended to define the future service contract.
  /** ISO date. Null until a gating item sets it. */
  announcement_date: string | null;

packages/shared/src/enums/formation.enum.spec.ts:67

  • satisfies FormationTemplate is not checked by either gate: this spec is excluded from tsc, and Vitest/esbuild erases the type expression. An incompatible change to FormationTemplate therefore still passes while this test continues to claim the shape round-trips. Move the fixture into a TypeScript input included by check-types, or add a dedicated type-test configuration.
    } satisfies FormationTemplate;

parent_project_name: string;
/** Present only for a `child_project` — the foundation/project this formation nests under, for the queue's indented display. */
parent_formation_name?: string;
entity_type: FormationEntityType;
Comment on lines +66 to +67
/** First not-done gating item's title, precomputed for the queue's "Blocking" column. */
blocking_item_title: string | null;
Comment on lines +216 to +226
/** Per-`sub_stage` counts for the queue's filter pills. */
export type FormationQueueTiles = Record<FormationSubStage, number> & {
total: number;
foundations: number;
child_projects: number;
};

/** Response body for `GET /api/formations`. */
export interface FormationsQueueResponse {
tiles: FormationQueueTiles;
rows: Formation[];
Comment on lines +125 to +126
/** Required and logged when a gating item is skipped. */
skip_reason: string | null;
@dealako

dealako commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@manishdixitlfx thanks for the rebase. That was the blocking item.

👏 Nice work. You rebased onto main and kept both barrel exports (id-migration.constants and formation.constants), so the add/add on packages/shared/src/constants/index.ts is gone. is_activating now says it is every gating item done, which is the "Gates cleared" half, not the full #1957 predicate. That half of the last round is closed.

New Copilot threads (after the rebase)

No new CodeRabbit or Bugbot findings this round.

Approved with minor comments

@dealako dealako left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase onto main cleared the blocking barrel conflict. Approving.

One leftover minor: announcement_date still says a gating item sets it. PCC owns that field. Copilot's thread already covers it: #2175 (comment)

@manishdixitlfx
manishdixitlfx added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 0a4b438 Sep 4, 2026
15 checks passed
@manishdixitlfx
manishdixitlfx deleted the feat/issue-2163 branch September 4, 2026 21:30
manishdixitlfx added a commit that referenced this pull request Sep 4, 2026
…ary (GH-1958)

Migrate onto the canonical shared Formation types from GH-2163/PR #2175
and close three product gaps: every checklist row is now actionable via
a status-control menu and overflow menu (assign/due date/skip), the new
awaiting_acceptance status is handled end-to-end (Accept flow, correct
counts, blue segment color), and gate-related copy is unified to
"Required for Active" across the row, drawer, readiness strip, and
queue table.

Refs #1958

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
manishdixitlfx added a commit that referenced this pull request Sep 7, 2026
…ary (GH-1958)

Migrate onto the canonical shared Formation types from GH-2163/PR #2175
and close three product gaps: every checklist row is now actionable via
a status-control menu and overflow menu (assign/due date/skip), the new
awaiting_acceptance status is handled end-to-end (Accept flow, correct
counts, blue segment color), and gate-related copy is unified to
"Required for Active" across the row, drawer, readiness strip, and
queue table.

Refs #1958

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
manishdixitlfx added a commit that referenced this pull request Sep 8, 2026
…ary (GH-1958)

Migrate onto the canonical shared Formation types from GH-2163/PR #2175
and close three product gaps: every checklist row is now actionable via
a status-control menu and overflow menu (assign/due date/skip), the new
awaiting_acceptance status is handled end-to-end (Accept flow, correct
counts, blue segment color), and gate-related copy is unified to
"Required for Active" across the row, drawer, readiness strip, and
queue table.

Refs #1958

Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Land canonical formation shared types on main before the feature branches merge

4 participants