@beyondwork/docx-react-component 1.0.105 → 1.0.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +10 -2
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-reference.ts +28 -0
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +310 -6
- package/src/api/v3/ai/attach.ts +29 -4
- package/src/api/v3/ai/bundle.ts +6 -2
- package/src/api/v3/ai/inspect.ts +6 -2
- package/src/api/v3/ai/replacement.ts +112 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +8 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +202 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +17 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +119 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +147 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +279 -115
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +661 -16
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +188 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +23 -4
- package/src/runtime/layout/paginated-layout-engine.ts +149 -15
- package/src/runtime/layout/project-block-fragments.ts +351 -14
- package/src/runtime/layout/public-facet.ts +162 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +4 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +386 -0
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +130 -0
- package/src/ui/ui-controller-factory.ts +17 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CommentSidebarSnapshot,
|
|
3
|
+
OverlayKind,
|
|
4
|
+
SuggestionsSnapshot,
|
|
5
|
+
TrackedChangesSnapshot,
|
|
6
|
+
WorkflowMarkupSnapshot,
|
|
7
|
+
WorkflowScopeSnapshot,
|
|
8
|
+
} from "../../api/public-types.ts";
|
|
9
|
+
import { ISSUE_METADATA_ID } from "../../api/public-types.ts";
|
|
10
|
+
import type {
|
|
11
|
+
WorkflowOverlayLaneEntry,
|
|
12
|
+
WorkflowOverlayLaneKind,
|
|
13
|
+
WorkflowOverlayLaneSnapshot,
|
|
14
|
+
WorkflowOverlayLaneStatus,
|
|
15
|
+
} from "./overlay-lane-types.ts";
|
|
16
|
+
|
|
17
|
+
export type WorkflowReviewOverlayLaneKind = Extract<
|
|
18
|
+
WorkflowOverlayLaneKind,
|
|
19
|
+
"redlines" | "comments" | "issues" | "field-scopes" | "broad-scopes" | "presence"
|
|
20
|
+
>;
|
|
21
|
+
export type WorkflowReviewOverlayLaneSnapshot = WorkflowOverlayLaneSnapshot;
|
|
22
|
+
export type WorkflowReviewOverlayLaneListener = (
|
|
23
|
+
snapshot: WorkflowReviewOverlayLaneSnapshot,
|
|
24
|
+
) => void;
|
|
25
|
+
export type WorkflowReviewOverlayLaneRefreshEvent = {
|
|
26
|
+
readonly type:
|
|
27
|
+
| "ready"
|
|
28
|
+
| "comment_added"
|
|
29
|
+
| "comment_resolved"
|
|
30
|
+
| "comments_changed"
|
|
31
|
+
| "change_authored"
|
|
32
|
+
| "change_accepted"
|
|
33
|
+
| "change_rejected"
|
|
34
|
+
| "suggestion_authored"
|
|
35
|
+
| "suggestion_updated"
|
|
36
|
+
| "workflow_metadata_changed"
|
|
37
|
+
| "workflow_overlay_changed"
|
|
38
|
+
| "workflow_active_work_item_changed"
|
|
39
|
+
| "workflow_shared_state_changed"
|
|
40
|
+
| "workflow_visibility_policy_changed"
|
|
41
|
+
| "workflow_markup_mode_policy_changed"
|
|
42
|
+
| "toc_auto_refreshed"
|
|
43
|
+
| "warning_added"
|
|
44
|
+
| "warning_cleared"
|
|
45
|
+
| "error"
|
|
46
|
+
| string;
|
|
47
|
+
readonly kind?: unknown;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export interface WorkflowReviewOverlayLaneInput {
|
|
51
|
+
readonly comments?: CommentSidebarSnapshot;
|
|
52
|
+
readonly trackedChanges?: TrackedChangesSnapshot;
|
|
53
|
+
readonly suggestions?: SuggestionsSnapshot | null;
|
|
54
|
+
readonly workflowScope?: WorkflowScopeSnapshot;
|
|
55
|
+
readonly workflowMarkup?: WorkflowMarkupSnapshot;
|
|
56
|
+
readonly revision?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Layer-06 data handoff for L10/L11 PE2 overlay lanes.
|
|
61
|
+
*
|
|
62
|
+
* This projector emits plain lane snapshots only. It does not resolve geometry,
|
|
63
|
+
* read DOM state, subscribe to awareness, dispatch PM transactions, or wake
|
|
64
|
+
* layout. Presence is intentionally unavailable here because live cursor/
|
|
65
|
+
* collaborator state is sourced from Yjs awareness, not durable workflow truth.
|
|
66
|
+
*/
|
|
67
|
+
export function projectWorkflowReviewOverlayLane(
|
|
68
|
+
kind: WorkflowReviewOverlayLaneKind,
|
|
69
|
+
input: WorkflowReviewOverlayLaneInput,
|
|
70
|
+
): WorkflowOverlayLaneSnapshot {
|
|
71
|
+
if (kind === "presence") {
|
|
72
|
+
return {
|
|
73
|
+
kind,
|
|
74
|
+
status: "unavailable",
|
|
75
|
+
entries: [],
|
|
76
|
+
revision: input.revision ?? 0,
|
|
77
|
+
source: "awareness",
|
|
78
|
+
reason: "presence lane is sourced from Yjs awareness, not durable workflow/review state",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const entries = projectEntries(kind, input);
|
|
83
|
+
return {
|
|
84
|
+
kind,
|
|
85
|
+
status: summarizeStatus(entries),
|
|
86
|
+
entries,
|
|
87
|
+
revision: input.revision ?? 0,
|
|
88
|
+
source: "workflow",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function shouldRefreshWorkflowReviewOverlayLane(
|
|
93
|
+
kind: WorkflowReviewOverlayLaneKind,
|
|
94
|
+
event: WorkflowReviewOverlayLaneRefreshEvent,
|
|
95
|
+
): boolean {
|
|
96
|
+
if (kind === "presence") return false;
|
|
97
|
+
switch (event.type) {
|
|
98
|
+
case "ready":
|
|
99
|
+
return true;
|
|
100
|
+
case "comment_added":
|
|
101
|
+
case "comment_resolved":
|
|
102
|
+
case "comments_changed":
|
|
103
|
+
return kind === "comments";
|
|
104
|
+
case "change_authored":
|
|
105
|
+
case "change_accepted":
|
|
106
|
+
case "change_rejected":
|
|
107
|
+
case "suggestion_authored":
|
|
108
|
+
case "suggestion_updated":
|
|
109
|
+
return kind === "redlines";
|
|
110
|
+
case "workflow_metadata_changed":
|
|
111
|
+
return kind === "issues" || kind === "broad-scopes";
|
|
112
|
+
case "workflow_overlay_changed":
|
|
113
|
+
case "workflow_active_work_item_changed":
|
|
114
|
+
case "workflow_shared_state_changed":
|
|
115
|
+
return kind === "issues" || kind === "broad-scopes";
|
|
116
|
+
case "workflow_visibility_policy_changed":
|
|
117
|
+
return shouldRefreshForVisibilityPolicy(
|
|
118
|
+
kind,
|
|
119
|
+
isOverlayKind(event.kind) ? event.kind : undefined,
|
|
120
|
+
);
|
|
121
|
+
case "workflow_markup_mode_policy_changed":
|
|
122
|
+
return kind === "redlines";
|
|
123
|
+
case "toc_auto_refreshed":
|
|
124
|
+
case "warning_added":
|
|
125
|
+
case "warning_cleared":
|
|
126
|
+
case "error":
|
|
127
|
+
return kind === "field-scopes";
|
|
128
|
+
default:
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function areWorkflowReviewOverlayLaneSnapshotsEqual(
|
|
134
|
+
left: WorkflowReviewOverlayLaneSnapshot,
|
|
135
|
+
right: WorkflowReviewOverlayLaneSnapshot,
|
|
136
|
+
): boolean {
|
|
137
|
+
return (
|
|
138
|
+
left.kind === right.kind &&
|
|
139
|
+
left.status === right.status &&
|
|
140
|
+
left.source === right.source &&
|
|
141
|
+
left.reason === right.reason &&
|
|
142
|
+
entriesEqual(left.entries, right.entries)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isOverlayKind(value: unknown): value is OverlayKind {
|
|
147
|
+
return (
|
|
148
|
+
value === "scope-rail" ||
|
|
149
|
+
value === "comments" ||
|
|
150
|
+
value === "tracked-changes" ||
|
|
151
|
+
value === "suggestions" ||
|
|
152
|
+
value === "debug-panel" ||
|
|
153
|
+
value === "presence"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function shouldRefreshForVisibilityPolicy(
|
|
158
|
+
kind: Exclude<WorkflowReviewOverlayLaneKind, "presence">,
|
|
159
|
+
overlayKind: OverlayKind | undefined,
|
|
160
|
+
): boolean {
|
|
161
|
+
switch (overlayKind) {
|
|
162
|
+
case "comments":
|
|
163
|
+
return kind === "comments";
|
|
164
|
+
case "tracked-changes":
|
|
165
|
+
case "suggestions":
|
|
166
|
+
return kind === "redlines";
|
|
167
|
+
case "scope-rail":
|
|
168
|
+
return kind === "broad-scopes";
|
|
169
|
+
case "presence":
|
|
170
|
+
case "debug-panel":
|
|
171
|
+
case undefined:
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function projectEntries(
|
|
177
|
+
kind: Exclude<WorkflowReviewOverlayLaneKind, "presence">,
|
|
178
|
+
input: WorkflowReviewOverlayLaneInput,
|
|
179
|
+
): WorkflowOverlayLaneEntry[] {
|
|
180
|
+
switch (kind) {
|
|
181
|
+
case "comments":
|
|
182
|
+
return (input.comments?.threads ?? []).map((thread) => ({
|
|
183
|
+
id: thread.commentId,
|
|
184
|
+
status: thread.status === "detached" ? "requires-rehydration" : "resolved",
|
|
185
|
+
anchor: { kind: "comment", value: thread.commentId },
|
|
186
|
+
reason: thread.detachedReason,
|
|
187
|
+
data: compactData({
|
|
188
|
+
laneItemKind: "comment",
|
|
189
|
+
commentId: thread.commentId,
|
|
190
|
+
editorAnchor: thread.anchor,
|
|
191
|
+
status: thread.status,
|
|
192
|
+
createdAt: thread.createdAt,
|
|
193
|
+
createdBy: thread.createdBy,
|
|
194
|
+
entryCount: thread.entryCount,
|
|
195
|
+
warningCount: thread.warningCount,
|
|
196
|
+
linkedRevisionId: thread.linkedRevisionId,
|
|
197
|
+
excerpt: thread.excerpt,
|
|
198
|
+
anchorLabel: thread.anchorLabel,
|
|
199
|
+
}),
|
|
200
|
+
}));
|
|
201
|
+
case "redlines":
|
|
202
|
+
return (input.trackedChanges?.revisions ?? []).map((revision) => ({
|
|
203
|
+
id: revision.revisionId,
|
|
204
|
+
status: revision.status === "detached" ? "requires-rehydration" : "resolved",
|
|
205
|
+
anchor: { kind: "revision", value: revision.revisionId },
|
|
206
|
+
reason: revision.preserveOnlyReason,
|
|
207
|
+
data: compactData({
|
|
208
|
+
laneItemKind: "revision",
|
|
209
|
+
revisionId: revision.revisionId,
|
|
210
|
+
editorAnchor: revision.anchor,
|
|
211
|
+
revisionKind: revision.kind,
|
|
212
|
+
semanticKind: revision.semanticKind,
|
|
213
|
+
suggestionId: revision.suggestionId,
|
|
214
|
+
status: revision.status,
|
|
215
|
+
actionability: revision.actionability,
|
|
216
|
+
authorId: revision.authorId,
|
|
217
|
+
createdAt: revision.createdAt,
|
|
218
|
+
label: revision.label,
|
|
219
|
+
excerpt: revision.excerpt,
|
|
220
|
+
storyTarget: revision.storyTarget,
|
|
221
|
+
canAccept: revision.canAccept,
|
|
222
|
+
canReject: revision.canReject,
|
|
223
|
+
warningCount: revision.warningCount,
|
|
224
|
+
commentThreadIds: revision.commentThreadIds,
|
|
225
|
+
}),
|
|
226
|
+
}));
|
|
227
|
+
case "issues":
|
|
228
|
+
return projectIssueEntries(input);
|
|
229
|
+
case "field-scopes":
|
|
230
|
+
return (input.workflowMarkup?.fields ?? []).map((field) => ({
|
|
231
|
+
id: `field:${field.fieldIndex}`,
|
|
232
|
+
status: "resolved",
|
|
233
|
+
data: compactData({
|
|
234
|
+
laneItemKind: "field",
|
|
235
|
+
fieldIndex: field.fieldIndex,
|
|
236
|
+
editorAnchor: field.anchor,
|
|
237
|
+
fieldFamily: field.fieldFamily,
|
|
238
|
+
fieldTarget: field.fieldTarget,
|
|
239
|
+
refreshStatus: field.refreshStatus,
|
|
240
|
+
displayText: field.displayText,
|
|
241
|
+
label: field.label,
|
|
242
|
+
storyTarget: field.storyTarget,
|
|
243
|
+
}),
|
|
244
|
+
}));
|
|
245
|
+
case "broad-scopes":
|
|
246
|
+
return [
|
|
247
|
+
...(input.workflowScope?.scopes ?? []).map((scope) => ({
|
|
248
|
+
id: scope.scopeId,
|
|
249
|
+
status: "resolved" as const,
|
|
250
|
+
anchor: { kind: "scope" as const, value: scope.scopeId },
|
|
251
|
+
data: compactData({
|
|
252
|
+
laneItemKind: "workflow-scope",
|
|
253
|
+
scopeId: scope.scopeId,
|
|
254
|
+
editorAnchor: scope.anchor,
|
|
255
|
+
mode: scope.mode,
|
|
256
|
+
label: scope.label,
|
|
257
|
+
domain: scope.domain,
|
|
258
|
+
workItemId: scope.workItemId,
|
|
259
|
+
visibility: scope.visibility,
|
|
260
|
+
guardPolicy: scope.guardPolicy,
|
|
261
|
+
storyTarget: scope.storyTarget,
|
|
262
|
+
metadataRefs: scope.metadataRefs,
|
|
263
|
+
}),
|
|
264
|
+
})),
|
|
265
|
+
...(input.workflowScope?.candidates ?? []).map((candidate) => ({
|
|
266
|
+
id: candidate.candidateId,
|
|
267
|
+
status: "resolved" as const,
|
|
268
|
+
data: compactData({
|
|
269
|
+
laneItemKind: "workflow-candidate",
|
|
270
|
+
candidateId: candidate.candidateId,
|
|
271
|
+
editorAnchor: candidate.anchor,
|
|
272
|
+
label: candidate.label,
|
|
273
|
+
source: candidate.source,
|
|
274
|
+
storyTarget: candidate.storyTarget,
|
|
275
|
+
}),
|
|
276
|
+
})),
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function projectIssueEntries(input: WorkflowReviewOverlayLaneInput): WorkflowOverlayLaneEntry[] {
|
|
282
|
+
const suggestionGroupsByIssueId = new Map<string, string[]>();
|
|
283
|
+
for (const group of input.suggestions?.groups ?? []) {
|
|
284
|
+
if (!group.issueId) continue;
|
|
285
|
+
const ids = suggestionGroupsByIssueId.get(group.issueId) ?? [];
|
|
286
|
+
ids.push(group.groupId);
|
|
287
|
+
suggestionGroupsByIssueId.set(group.issueId, ids);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return (input.workflowMarkup?.metadata ?? [])
|
|
291
|
+
.filter((entry) => entry.metadataId === ISSUE_METADATA_ID)
|
|
292
|
+
.map((entry) => {
|
|
293
|
+
const issueValue = isRecord(entry.value) ? entry.value : undefined;
|
|
294
|
+
const issueId = typeof issueValue?.issueId === "string" ? issueValue.issueId : undefined;
|
|
295
|
+
return {
|
|
296
|
+
id: issueId ?? entry.entryId,
|
|
297
|
+
status: "resolved",
|
|
298
|
+
anchor: entry.scopeId ? { kind: "scope", value: entry.scopeId } : undefined,
|
|
299
|
+
data: compactData({
|
|
300
|
+
laneItemKind: "issue",
|
|
301
|
+
entryId: entry.entryId,
|
|
302
|
+
metadataId: entry.metadataId,
|
|
303
|
+
issueId,
|
|
304
|
+
editorAnchor: entry.anchor,
|
|
305
|
+
scopeId: entry.scopeId,
|
|
306
|
+
workItemId: entry.workItemId,
|
|
307
|
+
label: entry.label,
|
|
308
|
+
title: typeof issueValue?.title === "string" ? issueValue.title : undefined,
|
|
309
|
+
topic: typeof issueValue?.topic === "string" ? issueValue.topic : undefined,
|
|
310
|
+
severity: typeof issueValue?.severity === "string" ? issueValue.severity : undefined,
|
|
311
|
+
checklistState:
|
|
312
|
+
typeof issueValue?.checklistState === "string" ? issueValue.checklistState : undefined,
|
|
313
|
+
suggestionGroupIds: issueId ? suggestionGroupsByIssueId.get(issueId) ?? [] : [],
|
|
314
|
+
persistence: entry.persistence,
|
|
315
|
+
metadataPersistence: entry.metadataPersistence,
|
|
316
|
+
storageRef: entry.storageRef,
|
|
317
|
+
metadataVersion: entry.metadataVersion,
|
|
318
|
+
}),
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function summarizeStatus(entries: readonly WorkflowOverlayLaneEntry[]): WorkflowOverlayLaneStatus {
|
|
324
|
+
return entries.some((entry) => entry.status === "requires-rehydration")
|
|
325
|
+
? "requires-rehydration"
|
|
326
|
+
: "resolved";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function entriesEqual(
|
|
330
|
+
left: readonly WorkflowOverlayLaneEntry[],
|
|
331
|
+
right: readonly WorkflowOverlayLaneEntry[],
|
|
332
|
+
): boolean {
|
|
333
|
+
if (left.length !== right.length) return false;
|
|
334
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
335
|
+
const leftEntry = left[index];
|
|
336
|
+
const rightEntry = right[index];
|
|
337
|
+
if (!leftEntry || !rightEntry) return false;
|
|
338
|
+
if (
|
|
339
|
+
leftEntry.id !== rightEntry.id ||
|
|
340
|
+
leftEntry.status !== rightEntry.status ||
|
|
341
|
+
leftEntry.reason !== rightEntry.reason ||
|
|
342
|
+
!jsonValueEqual(leftEntry.anchor, rightEntry.anchor) ||
|
|
343
|
+
!jsonValueEqual(leftEntry.rects, rightEntry.rects) ||
|
|
344
|
+
!jsonValueEqual(leftEntry.data, rightEntry.data)
|
|
345
|
+
) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function jsonValueEqual(left: unknown, right: unknown): boolean {
|
|
353
|
+
return stableJson(left) === stableJson(right);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function stableJson(value: unknown): string {
|
|
357
|
+
return JSON.stringify(sortJsonValue(value));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function sortJsonValue(value: unknown): unknown {
|
|
361
|
+
if (Array.isArray(value)) {
|
|
362
|
+
return value.map(sortJsonValue);
|
|
363
|
+
}
|
|
364
|
+
if (!isRecord(value)) {
|
|
365
|
+
return value;
|
|
366
|
+
}
|
|
367
|
+
const sorted: Record<string, unknown> = {};
|
|
368
|
+
for (const key of Object.keys(value).sort()) {
|
|
369
|
+
sorted[key] = sortJsonValue(value[key]);
|
|
370
|
+
}
|
|
371
|
+
return sorted;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function compactData(input: Record<string, unknown>): Readonly<Record<string, unknown>> {
|
|
375
|
+
const output: Record<string, unknown> = {};
|
|
376
|
+
for (const [key, value] of Object.entries(input)) {
|
|
377
|
+
if (value !== undefined) {
|
|
378
|
+
output[key] = value;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return output;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
385
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
386
|
+
}
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
*
|
|
23
23
|
* What it does NOT own (by design — those belong to the coordinator):
|
|
24
24
|
* - interaction-guard / workflow-scope / workflow-markup snapshot
|
|
25
|
-
* caches (require runtime-scope deps like
|
|
26
|
-
*
|
|
25
|
+
* caches (require runtime-scope deps like render snapshot,
|
|
26
|
+
* selection, protection, and active story).
|
|
27
27
|
* - blocked-reason composition + scope-matching heuristics (require
|
|
28
28
|
* protection snapshot + viewState.documentMode).
|
|
29
29
|
* - rail / card composition (requires page graph + render frame).
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
export type WorkflowRedlinePosture = "all-markup" | "final" | "original" | "unknown";
|
|
2
|
+
|
|
3
|
+
export interface WorkflowRedlineDatasetDocumentLike {
|
|
4
|
+
readonly docId: string;
|
|
5
|
+
readonly status: string;
|
|
6
|
+
readonly pages: number;
|
|
7
|
+
readonly revisions: number;
|
|
8
|
+
readonly comments: number;
|
|
9
|
+
readonly fields?: number;
|
|
10
|
+
readonly uiaLines?: number;
|
|
11
|
+
readonly datasetPath?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WorkflowRedlineDatasetManifestLike {
|
|
15
|
+
readonly schemaVersion: string;
|
|
16
|
+
readonly sourceRun: string;
|
|
17
|
+
readonly captureMatrix?: {
|
|
18
|
+
readonly markupModes?: readonly string[];
|
|
19
|
+
};
|
|
20
|
+
readonly targetedWord?: {
|
|
21
|
+
readonly version?: string;
|
|
22
|
+
readonly build?: string;
|
|
23
|
+
readonly activePrinter?: string;
|
|
24
|
+
};
|
|
25
|
+
readonly documents: readonly WorkflowRedlineDatasetDocumentLike[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface WorkflowRedlineFieldIdentityTotalsLike {
|
|
29
|
+
readonly wordFields: number;
|
|
30
|
+
readonly joined: number;
|
|
31
|
+
readonly missingSourceField: number;
|
|
32
|
+
readonly missingCanonicalField: number;
|
|
33
|
+
readonly missingRuntimeRegion: number;
|
|
34
|
+
readonly actionableL04: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type WorkflowRedlinePostureFactId =
|
|
38
|
+
| "all-markup-review-posture"
|
|
39
|
+
| "revision-comment-counts"
|
|
40
|
+
| "clean-all-markup-measurement-handoff"
|
|
41
|
+
| "redline-field-identity-posture";
|
|
42
|
+
|
|
43
|
+
export type WorkflowRedlinePostureRepresentation =
|
|
44
|
+
| "durable-workflow-review-posture"
|
|
45
|
+
| "measurement-handoff"
|
|
46
|
+
| "field-region-posture-handoff"
|
|
47
|
+
| "blocked-until-identity-join";
|
|
48
|
+
|
|
49
|
+
export interface WorkflowRedlinePostureFact {
|
|
50
|
+
readonly factId: WorkflowRedlinePostureFactId;
|
|
51
|
+
readonly representation: WorkflowRedlinePostureRepresentation;
|
|
52
|
+
readonly posture: WorkflowRedlinePosture;
|
|
53
|
+
readonly sourceRun: string;
|
|
54
|
+
readonly l06Surfaces: readonly string[];
|
|
55
|
+
readonly downstreamRoutes: readonly string[];
|
|
56
|
+
readonly evidence: Readonly<Record<string, number | string | boolean>>;
|
|
57
|
+
readonly note: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface WorkflowRedlinePostureCalibration {
|
|
61
|
+
readonly schemaVersion: "l06-redline-posture-calibration/v0";
|
|
62
|
+
readonly facts: readonly WorkflowRedlinePostureFact[];
|
|
63
|
+
readonly summary: {
|
|
64
|
+
readonly documentCount: number;
|
|
65
|
+
readonly availableDocumentCount: number;
|
|
66
|
+
readonly pageCount: number;
|
|
67
|
+
readonly revisionCount: number;
|
|
68
|
+
readonly commentCount: number;
|
|
69
|
+
readonly fieldCount: number;
|
|
70
|
+
readonly uiaLineCount: number;
|
|
71
|
+
readonly posture: WorkflowRedlinePosture;
|
|
72
|
+
readonly downstreamRouteCount: number;
|
|
73
|
+
readonly l04ActionableFieldRows: number;
|
|
74
|
+
readonly ownsGeometry: false;
|
|
75
|
+
readonly ownsVisualPaint: false;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface WorkflowRedlinePostureCalibrationInput {
|
|
80
|
+
readonly manifest: WorkflowRedlineDatasetManifestLike;
|
|
81
|
+
readonly fieldIdentityTotals?: WorkflowRedlineFieldIdentityTotalsLike;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface WorkflowRedlinePostureTotals {
|
|
85
|
+
readonly documentCount: number;
|
|
86
|
+
readonly availableDocumentCount: number;
|
|
87
|
+
readonly pageCount: number;
|
|
88
|
+
readonly revisionCount: number;
|
|
89
|
+
readonly commentCount: number;
|
|
90
|
+
readonly fieldCount: number;
|
|
91
|
+
readonly uiaLineCount: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function calibrateWorkflowReviewRedlinePosture(
|
|
95
|
+
input: WorkflowRedlinePostureCalibrationInput,
|
|
96
|
+
): WorkflowRedlinePostureCalibration {
|
|
97
|
+
const documents = input.manifest.documents;
|
|
98
|
+
const availableDocuments = documents.filter((document) => document.status === "available");
|
|
99
|
+
const posture = resolvePosture(input.manifest.captureMatrix?.markupModes);
|
|
100
|
+
const totals = {
|
|
101
|
+
documentCount: documents.length,
|
|
102
|
+
availableDocumentCount: availableDocuments.length,
|
|
103
|
+
pageCount: sum(availableDocuments, (document) => document.pages),
|
|
104
|
+
revisionCount: sum(availableDocuments, (document) => document.revisions),
|
|
105
|
+
commentCount: sum(availableDocuments, (document) => document.comments),
|
|
106
|
+
fieldCount: sum(availableDocuments, (document) => document.fields ?? 0),
|
|
107
|
+
uiaLineCount: sum(availableDocuments, (document) => document.uiaLines ?? 0),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const facts = [
|
|
111
|
+
buildAllMarkupPostureFact(input.manifest, posture, totals),
|
|
112
|
+
buildReviewCountFact(input.manifest, posture, totals),
|
|
113
|
+
buildMeasurementHandoffFact(input.manifest, posture, totals),
|
|
114
|
+
buildFieldIdentityFact(input.manifest, posture, totals, input.fieldIdentityTotals),
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
schemaVersion: "l06-redline-posture-calibration/v0",
|
|
119
|
+
facts,
|
|
120
|
+
summary: {
|
|
121
|
+
...totals,
|
|
122
|
+
posture,
|
|
123
|
+
downstreamRouteCount: new Set(facts.flatMap((fact) => fact.downstreamRoutes)).size,
|
|
124
|
+
l04ActionableFieldRows: input.fieldIdentityTotals?.actionableL04 ?? 0,
|
|
125
|
+
ownsGeometry: false,
|
|
126
|
+
ownsVisualPaint: false,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildAllMarkupPostureFact(
|
|
132
|
+
manifest: WorkflowRedlineDatasetManifestLike,
|
|
133
|
+
posture: WorkflowRedlinePosture,
|
|
134
|
+
totals: WorkflowRedlinePostureTotals,
|
|
135
|
+
): WorkflowRedlinePostureFact {
|
|
136
|
+
return {
|
|
137
|
+
factId: "all-markup-review-posture",
|
|
138
|
+
representation: "durable-workflow-review-posture",
|
|
139
|
+
posture,
|
|
140
|
+
sourceRun: manifest.sourceRun,
|
|
141
|
+
l06Surfaces: [
|
|
142
|
+
"WorkflowMarkupModePolicy",
|
|
143
|
+
"WorkflowMarkupSnapshot.revisions",
|
|
144
|
+
"TrackedChangesSnapshot.revisions",
|
|
145
|
+
],
|
|
146
|
+
downstreamRoutes: ["L04 redline measurement posture", "L11 redline overlay paint"],
|
|
147
|
+
evidence: {
|
|
148
|
+
documents: totals.documentCount,
|
|
149
|
+
availableDocuments: totals.availableDocumentCount,
|
|
150
|
+
wordVersion: manifest.targetedWord?.version ?? "unknown",
|
|
151
|
+
wordBuild: manifest.targetedWord?.build ?? "unknown",
|
|
152
|
+
},
|
|
153
|
+
note:
|
|
154
|
+
"The redline Word JSON dataset is captured in all-markup posture; L06 names that durable review posture before layout or paint consume it.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function buildReviewCountFact(
|
|
159
|
+
manifest: WorkflowRedlineDatasetManifestLike,
|
|
160
|
+
posture: WorkflowRedlinePosture,
|
|
161
|
+
totals: WorkflowRedlinePostureTotals,
|
|
162
|
+
): WorkflowRedlinePostureFact {
|
|
163
|
+
return {
|
|
164
|
+
factId: "revision-comment-counts",
|
|
165
|
+
representation: "durable-workflow-review-posture",
|
|
166
|
+
posture,
|
|
167
|
+
sourceRun: manifest.sourceRun,
|
|
168
|
+
l06Surfaces: [
|
|
169
|
+
"WorkflowMarkupSnapshot.revisions",
|
|
170
|
+
"WorkflowMarkupSnapshot.comments",
|
|
171
|
+
"TrackedChangesSnapshot.revisions",
|
|
172
|
+
"CommentSidebarSnapshot.threads",
|
|
173
|
+
],
|
|
174
|
+
downstreamRoutes: ["L11 review/comment lane visibility", "L08 comment/revision scope posture"],
|
|
175
|
+
evidence: {
|
|
176
|
+
revisions: totals.revisionCount,
|
|
177
|
+
comments: totals.commentCount,
|
|
178
|
+
documentsWithReviewData: totals.availableDocumentCount,
|
|
179
|
+
},
|
|
180
|
+
note:
|
|
181
|
+
"Revision and comment totals are L06 review-store facts; geometry and balloon placement stay with downstream consumers.",
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildMeasurementHandoffFact(
|
|
186
|
+
manifest: WorkflowRedlineDatasetManifestLike,
|
|
187
|
+
posture: WorkflowRedlinePosture,
|
|
188
|
+
totals: WorkflowRedlinePostureTotals,
|
|
189
|
+
): WorkflowRedlinePostureFact {
|
|
190
|
+
return {
|
|
191
|
+
factId: "clean-all-markup-measurement-handoff",
|
|
192
|
+
representation: "measurement-handoff",
|
|
193
|
+
posture,
|
|
194
|
+
sourceRun: manifest.sourceRun,
|
|
195
|
+
l06Surfaces: ["EffectiveLayoutFormatting.revisionPosture", "WorkflowMarkupModePolicy"],
|
|
196
|
+
downstreamRoutes: ["L04 clean-vs-all-markup page/frame delta", "L11 render-word assertion planning"],
|
|
197
|
+
evidence: {
|
|
198
|
+
pagesInAllMarkupPosture: totals.pageCount,
|
|
199
|
+
uiaLinesInAllMarkupPosture: totals.uiaLineCount,
|
|
200
|
+
ownsGeometry: false,
|
|
201
|
+
},
|
|
202
|
+
note:
|
|
203
|
+
"L06 names the measurement-affecting review posture; L04 owns page/frame deltas and L11 owns visual assertion planning.",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function buildFieldIdentityFact(
|
|
208
|
+
manifest: WorkflowRedlineDatasetManifestLike,
|
|
209
|
+
posture: WorkflowRedlinePosture,
|
|
210
|
+
totals: WorkflowRedlinePostureTotals,
|
|
211
|
+
fieldIdentityTotals: WorkflowRedlineFieldIdentityTotalsLike | undefined,
|
|
212
|
+
): WorkflowRedlinePostureFact {
|
|
213
|
+
const actionableRows = fieldIdentityTotals?.actionableL04 ?? 0;
|
|
214
|
+
return {
|
|
215
|
+
factId: "redline-field-identity-posture",
|
|
216
|
+
representation: actionableRows > 0 ? "field-region-posture-handoff" : "blocked-until-identity-join",
|
|
217
|
+
posture,
|
|
218
|
+
sourceRun: manifest.sourceRun,
|
|
219
|
+
l06Surfaces: ["WorkflowMarkupModePolicy", "WorkflowMarkupSnapshot.fields"],
|
|
220
|
+
downstreamRoutes: [
|
|
221
|
+
"L01 source field identity",
|
|
222
|
+
"L02 canonical field identity",
|
|
223
|
+
"L04 redline field-region measurement",
|
|
224
|
+
],
|
|
225
|
+
evidence: {
|
|
226
|
+
redlineWordFields: fieldIdentityTotals?.wordFields ?? totals.fieldCount,
|
|
227
|
+
sourceJoinedRows: fieldIdentityTotals?.joined ?? 0,
|
|
228
|
+
missingSourceField: fieldIdentityTotals?.missingSourceField ?? 0,
|
|
229
|
+
missingCanonicalField: fieldIdentityTotals?.missingCanonicalField ?? 0,
|
|
230
|
+
missingRuntimeRegion: fieldIdentityTotals?.missingRuntimeRegion ?? 0,
|
|
231
|
+
actionableL04: actionableRows,
|
|
232
|
+
},
|
|
233
|
+
note:
|
|
234
|
+
actionableRows > 0
|
|
235
|
+
? "L06 review posture is named and the field rows may proceed to L04 with identity joins present."
|
|
236
|
+
: "L06 review posture is named as all-markup, but redline field-region rows remain blocked on source/canonical/runtime identity joins.",
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function resolvePosture(markupModes: readonly string[] | undefined): WorkflowRedlinePosture {
|
|
241
|
+
if (!markupModes || markupModes.length !== 1) return "unknown";
|
|
242
|
+
switch (markupModes[0]) {
|
|
243
|
+
case "all":
|
|
244
|
+
case "all-markup":
|
|
245
|
+
return "all-markup";
|
|
246
|
+
case "final":
|
|
247
|
+
return "final";
|
|
248
|
+
case "original":
|
|
249
|
+
return "original";
|
|
250
|
+
default:
|
|
251
|
+
return "unknown";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function sum<T>(items: readonly T[], selector: (item: T) => number): number {
|
|
256
|
+
return items.reduce((total, item) => total + selector(item), 0);
|
|
257
|
+
}
|