Stop seeding 75% consensus on custom decision cards so wizard copy shows on the published rule.
Wire Back on the final-review chip editor to the same dismiss path as close, instead of a no-op. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -16,7 +16,10 @@ import {
|
||||
publishedMethodDisplayLabel,
|
||||
} from "./finalReviewChipPresets";
|
||||
import { isDocumentEntry } from "./documentEntryGuards";
|
||||
import { replaceMethodSectionsWithMethodSelections } from "./ruleSectionsFromMethodSelections";
|
||||
import {
|
||||
replaceMethodSectionsWithMethodSelections,
|
||||
withoutUnpublishedDecisionConsensus,
|
||||
} from "./ruleSectionsFromMethodSelections";
|
||||
import { templateCategoryToGroupKey } from "./templateReviewMapping";
|
||||
|
||||
export { isDocumentEntry } from "./documentEntryGuards";
|
||||
@@ -78,21 +81,25 @@ export type PublishedMethodSelections = {
|
||||
id: string;
|
||||
label: string;
|
||||
sections: CommunicationMethodDetailEntry;
|
||||
supportText?: string;
|
||||
}>;
|
||||
membership?: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
sections: MembershipMethodDetailEntry;
|
||||
supportText?: string;
|
||||
}>;
|
||||
decisionApproaches?: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
sections: DecisionApproachDetailEntry;
|
||||
supportText?: string;
|
||||
}>;
|
||||
conflictManagement?: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
sections: ConflictManagementDetailEntry;
|
||||
supportText?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -247,6 +254,14 @@ function pickMethodIds(
|
||||
return derived;
|
||||
}
|
||||
|
||||
function publishedRowSupportText(
|
||||
id: string,
|
||||
meta: CreateFlowState["customMethodCardMetaById"],
|
||||
): string | undefined {
|
||||
const t = meta?.[id]?.supportText?.trim();
|
||||
return t && t.length > 0 ? t : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge `selected*MethodIds` with any saved `{group}MethodDetailsById`
|
||||
* overrides authored on the final-review screen. Preset defaults from the
|
||||
@@ -270,6 +285,10 @@ export function buildMethodSelectionsForDocument(
|
||||
out.communication = commIds.map((id) => {
|
||||
const preset = communicationPresetFor(id);
|
||||
const override = state.communicationMethodDetailsById?.[id];
|
||||
const supportText = publishedRowSupportText(
|
||||
id,
|
||||
state.customMethodCardMetaById,
|
||||
);
|
||||
return {
|
||||
id,
|
||||
label: publishedMethodDisplayLabel(
|
||||
@@ -278,6 +297,7 @@ export function buildMethodSelectionsForDocument(
|
||||
state.customMethodCardMetaById,
|
||||
),
|
||||
sections: override ? { ...preset, ...override } : preset,
|
||||
...(supportText ? { supportText } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -290,6 +310,10 @@ export function buildMethodSelectionsForDocument(
|
||||
out.membership = memIds.map((id) => {
|
||||
const preset = membershipPresetFor(id);
|
||||
const override = state.membershipMethodDetailsById?.[id];
|
||||
const supportText = publishedRowSupportText(
|
||||
id,
|
||||
state.customMethodCardMetaById,
|
||||
);
|
||||
return {
|
||||
id,
|
||||
label: publishedMethodDisplayLabel(
|
||||
@@ -298,6 +322,7 @@ export function buildMethodSelectionsForDocument(
|
||||
state.customMethodCardMetaById,
|
||||
),
|
||||
sections: override ? { ...preset, ...override } : preset,
|
||||
...(supportText ? { supportText } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -310,6 +335,11 @@ export function buildMethodSelectionsForDocument(
|
||||
out.decisionApproaches = daIds.map((id) => {
|
||||
const preset = decisionApproachPresetFor(id);
|
||||
const override = state.decisionApproachDetailsById?.[id];
|
||||
const supportText = publishedRowSupportText(
|
||||
id,
|
||||
state.customMethodCardMetaById,
|
||||
);
|
||||
const merged = override ? { ...preset, ...override } : preset;
|
||||
return {
|
||||
id,
|
||||
label: publishedMethodDisplayLabel(
|
||||
@@ -317,7 +347,11 @@ export function buildMethodSelectionsForDocument(
|
||||
id,
|
||||
state.customMethodCardMetaById,
|
||||
),
|
||||
sections: override ? { ...preset, ...override } : preset,
|
||||
sections: withoutUnpublishedDecisionConsensus(
|
||||
{ ...merged },
|
||||
state.customMethodCardFieldBlocksById?.[id],
|
||||
) as DecisionApproachDetailEntry,
|
||||
...(supportText ? { supportText } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -330,6 +364,10 @@ export function buildMethodSelectionsForDocument(
|
||||
out.conflictManagement = cmIds.map((id) => {
|
||||
const preset = conflictManagementPresetFor(id);
|
||||
const override = state.conflictManagementDetailsById?.[id];
|
||||
const supportText = publishedRowSupportText(
|
||||
id,
|
||||
state.customMethodCardMetaById,
|
||||
);
|
||||
return {
|
||||
id,
|
||||
label: publishedMethodDisplayLabel(
|
||||
@@ -338,6 +376,7 @@ export function buildMethodSelectionsForDocument(
|
||||
state.customMethodCardMetaById,
|
||||
),
|
||||
sections: override ? { ...preset, ...override } : preset,
|
||||
...(supportText ? { supportText } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ export function membershipPresetFor(id: string): MembershipMethodDetailEntry {
|
||||
};
|
||||
}
|
||||
|
||||
/** Default consensus level used when presets omit a value (see DecisionApproachesScreen). */
|
||||
/** Default consensus level used when a **catalog** preset omits a value. */
|
||||
export const DECISION_CONSENSUS_LEVEL_DEFAULT = 75;
|
||||
|
||||
export function decisionApproachPresetFor(
|
||||
@@ -114,19 +114,22 @@ export function decisionApproachPresetFor(
|
||||
): DecisionApproachDetailEntry {
|
||||
const method = findMethod(decisionApproachesMessages, id);
|
||||
const s = method?.sections ?? {};
|
||||
return {
|
||||
const entry: DecisionApproachDetailEntry = {
|
||||
corePrinciple: asString(s.corePrinciple),
|
||||
applicableScope: asStringArray(s.applicableScope),
|
||||
selectedApplicableScope: [],
|
||||
stepByStepInstructions: asString(s.stepByStepInstructions),
|
||||
consensusLevel: asNumberClamped(
|
||||
objectionsDeadlocks: asString(s.objectionsDeadlocks),
|
||||
};
|
||||
if (method) {
|
||||
entry.consensusLevel = asNumberClamped(
|
||||
s.consensusLevel,
|
||||
0,
|
||||
100,
|
||||
DECISION_CONSENSUS_LEVEL_DEFAULT,
|
||||
),
|
||||
objectionsDeadlocks: asString(s.objectionsDeadlocks),
|
||||
};
|
||||
);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
export function conflictManagementPresetFor(
|
||||
|
||||
@@ -195,12 +195,19 @@ function mapFacetPrefillToWizardFieldBlocks(
|
||||
prefill.headings.stepByStepInstructions,
|
||||
prefill.draft.stepByStepInstructions,
|
||||
),
|
||||
{
|
||||
);
|
||||
if (
|
||||
typeof prefill.draft.consensusLevel === "number" &&
|
||||
facetPrefillHasContent(prefill)
|
||||
) {
|
||||
blocks.push({
|
||||
kind: "proportion",
|
||||
id: "facet-consensusLevel",
|
||||
blockTitle: prefill.headings.consensusLevel,
|
||||
defaultPercent: clampPercent(prefill.draft.consensusLevel),
|
||||
},
|
||||
});
|
||||
}
|
||||
blocks.push(
|
||||
textBlock(
|
||||
"facet-objectionsDeadlocks",
|
||||
prefill.headings.objectionsDeadlocks,
|
||||
|
||||
@@ -22,6 +22,7 @@ function customMethodCardMetaFromPublishedSelections(
|
||||
| Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
supportText?: string;
|
||||
}>
|
||||
| undefined,
|
||||
) => {
|
||||
@@ -32,7 +33,9 @@ function customMethodCardMetaFromPublishedSelections(
|
||||
if (methodLabelFor(groupKey, id).length > 0) continue;
|
||||
const label = typeof row.label === "string" ? row.label.trim() : "";
|
||||
if (!label) continue;
|
||||
meta[id] = { label, supportText: "" };
|
||||
const supportText =
|
||||
typeof row.supportText === "string" ? row.supportText : "";
|
||||
meta[id] = { label, supportText };
|
||||
}
|
||||
};
|
||||
absorb("communication", ms.communication);
|
||||
|
||||
@@ -78,6 +78,8 @@ export function labeledBlocksFromCustomMethodCardFieldBlocks(
|
||||
export type CommunityRuleEntryFromChipOptions = {
|
||||
consensusLevelKey?: string;
|
||||
customFieldBlocks?: CustomMethodCardFieldBlock[];
|
||||
/** Wizard step-2 policy description (`customMethodCardMetaById.supportText`). */
|
||||
supportText?: string;
|
||||
};
|
||||
|
||||
/** Canonical `categoryName` strings for method groups in published documents. */
|
||||
@@ -195,8 +197,64 @@ export function communityRuleEntryFromMethodChip(
|
||||
? labeledBlocksFromCustomMethodCardFieldBlocks(options.customFieldBlocks)
|
||||
: [];
|
||||
const blocks = [...presetBlocks, ...wizardBlocks];
|
||||
if (blocks.length === 0) return null;
|
||||
return { title, body: "", blocks };
|
||||
const description = nonEmptyTrimmed(options?.supportText);
|
||||
if (blocks.length === 0) {
|
||||
if (!description) return null;
|
||||
return { title, body: description };
|
||||
}
|
||||
return {
|
||||
title,
|
||||
body: description ?? "",
|
||||
blocks,
|
||||
};
|
||||
}
|
||||
|
||||
function decisionApproachHasPublishableFacetCopy(
|
||||
sections: Record<string, unknown>,
|
||||
): boolean {
|
||||
return Boolean(
|
||||
nonEmptyTrimmed(sections.corePrinciple) ||
|
||||
nonEmptyTrimmed(sections.stepByStepInstructions) ||
|
||||
nonEmptyTrimmed(sections.objectionsDeadlocks) ||
|
||||
formatScopePayload(sections.applicableScope) ||
|
||||
formatScopePayload(sections.selectedApplicableScope),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Catalog methods publish their consensus figure. User-authored custom cards
|
||||
* often seed `75` with empty facet copy — skip that unless the author actually
|
||||
* filled decision sections. Wizard field blocks (including proportion) are the
|
||||
* source of truth when present.
|
||||
*/
|
||||
function shouldPublishDecisionConsensusLevel(
|
||||
sections: Record<string, unknown>,
|
||||
customFieldBlocks?: CustomMethodCardFieldBlock[],
|
||||
): boolean {
|
||||
if (
|
||||
typeof sections.consensusLevel !== "number" ||
|
||||
Number.isNaN(sections.consensusLevel)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (customFieldBlocks && customFieldBlocks.length > 0) {
|
||||
return false;
|
||||
}
|
||||
return decisionApproachHasPublishableFacetCopy(sections);
|
||||
}
|
||||
|
||||
/** Drop seeded / wizard-superseded `consensusLevel` before publish or hydrate. */
|
||||
export function withoutUnpublishedDecisionConsensus(
|
||||
sections: Record<string, unknown>,
|
||||
customFieldBlocks?: CustomMethodCardFieldBlock[],
|
||||
): Record<string, unknown> {
|
||||
if (shouldPublishDecisionConsensusLevel(sections, customFieldBlocks)) {
|
||||
return sections;
|
||||
}
|
||||
if (!("consensusLevel" in sections)) return sections;
|
||||
const next = { ...sections };
|
||||
delete next.consensusLevel;
|
||||
return next;
|
||||
}
|
||||
|
||||
export function sectionFromCommunication(
|
||||
@@ -209,6 +267,7 @@ export function sectionFromCommunication(
|
||||
const sec = m.sections as unknown as Record<string, unknown>;
|
||||
const e = communityRuleEntryFromMethodChip(m.label, sec, COMM_LABELS, {
|
||||
customFieldBlocks: customFieldBlocksById?.[m.id],
|
||||
supportText: m.supportText,
|
||||
});
|
||||
if (e) entries.push(e);
|
||||
}
|
||||
@@ -227,6 +286,7 @@ export function sectionFromMembership(
|
||||
const sec = m.sections as unknown as Record<string, unknown>;
|
||||
const e = communityRuleEntryFromMethodChip(m.label, sec, MEM_LABELS, {
|
||||
customFieldBlocks: customFieldBlocksById?.[m.id],
|
||||
supportText: m.supportText,
|
||||
});
|
||||
if (e) entries.push(e);
|
||||
}
|
||||
@@ -254,10 +314,19 @@ export function sectionFromDecision(
|
||||
);
|
||||
if (scope) merged.applicableScope = scope;
|
||||
delete merged.selectedApplicableScope;
|
||||
const e = communityRuleEntryFromMethodChip(m.label, merged, DEC_LABELS, {
|
||||
consensusLevelKey: "consensusLevel",
|
||||
customFieldBlocks: customFieldBlocksById?.[m.id],
|
||||
});
|
||||
const e = communityRuleEntryFromMethodChip(
|
||||
m.label,
|
||||
withoutUnpublishedDecisionConsensus(
|
||||
merged,
|
||||
customFieldBlocksById?.[m.id],
|
||||
),
|
||||
DEC_LABELS,
|
||||
{
|
||||
consensusLevelKey: "consensusLevel",
|
||||
customFieldBlocks: customFieldBlocksById?.[m.id],
|
||||
supportText: m.supportText,
|
||||
},
|
||||
);
|
||||
if (e) entries.push(e);
|
||||
}
|
||||
return entries.length > 0
|
||||
@@ -281,6 +350,7 @@ export function sectionFromConflict(
|
||||
delete merged.selectedApplicableScope;
|
||||
const e = communityRuleEntryFromMethodChip(m.label, merged, CM_LABELS, {
|
||||
customFieldBlocks: customFieldBlocksById?.[m.id],
|
||||
supportText: m.supportText,
|
||||
});
|
||||
if (e) entries.push(e);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const decisionApproachDetailEntrySchema = z.object({
|
||||
applicableScope: z.array(z.string().max(2000)).max(50),
|
||||
selectedApplicableScope: z.array(z.string().max(2000)).max(50),
|
||||
stepByStepInstructions: z.string().max(8000),
|
||||
consensusLevel: z.number().int().min(0).max(100),
|
||||
consensusLevel: z.number().int().min(0).max(100).optional(),
|
||||
objectionsDeadlocks: z.string().max(8000),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user