diff --git a/src/pages/Assessments.js b/src/pages/Assessments.js index e017683..2a9649f 100644 --- a/src/pages/Assessments.js +++ b/src/pages/Assessments.js @@ -30,7 +30,7 @@ import { formatInlineMarkdown, stripMarkdown } from '../utils/markdownText'; import { bankCoverage, getBankProcedure, canResetToCommunity, resetToCommunityUpdate, sourceUrlFor } from '../utils/procedureBank'; import { expandProcedureText, derivePlatformsFromObservations } from '../utils/platformBank'; import { canUseProfileWithProvider, buildTailorPrompt, tailoredProvenance, deriveStackTargets, describeStackPlan, bankAttachObservation, wizardAttachObservation, deterministicTailorUpdate, pickerObservationUpdate, pickerAvailability } from '../utils/procedureTailor'; -import { buildEnvironmentMatrix, buildAttachPlan, cellKey, toggleCell, setColumn, columnFullySelected, columnTotal, countAttached, attachedCountForItem, availablePlatforms } from '../utils/environmentStep'; +import { buildEnvironmentMatrix, buildAttachPlan, cellKey, toggleCell, setColumn, columnFullySelected, columnTotal, countAttached, attachedCountForItem, availablePlatforms, environmentAttachCopy } from '../utils/environmentStep'; import { platformIdsFromInfrastructure } from '../utils/infraPresets'; import { getScoringScale, scoreBand, CMMI_LEVELS } from '../utils/scoringScale'; import { SYSTEM_NAME_MAX_LENGTH } from '../utils/externalLinks'; @@ -190,6 +190,10 @@ const Assessments = () => { () => countAttached(envMatrix, envSelections), [envMatrix, envSelections] ); + const envAttachCopy = useMemo( + () => environmentAttachCopy(useBankProcedures, envAttachedCount, envMatrix.totalAvailable), + [useBankProcedures, envAttachedCount, envMatrix.totalAvailable] + ); // Wizard Users step (issue #290): people in scope for the assessment. // Rows are { name, email, role } while editing; on create they become @@ -2709,7 +2713,7 @@ Format as a numbered list. Be specific and actionable.`; {envMatrix.noOfferCount > 0 && (

{envMatrix.noOfferCount} of your scoped items have no platform checks for - these platforms. Their community procedures attach as usual. + these platforms.{useBankProcedures ? ' Their community procedures attach as usual.' : ''}

)} @@ -2741,11 +2745,11 @@ Format as a numbered list. Be specific and actionable.`;

{scopeCoverage.covered.length} of {selectedScopeItems.size} selected items have community procedures

- {envPlatforms.length > 0 && ( -

- {envAttachedCount} platform {envAttachedCount === 1 ? 'check' : 'checks'} from - the Environment step {envAttachedCount === 1 ? 'attaches' : 'attach'} as - addenda ({envMatrix.totalAvailable} available). + {envPlatforms.length > 0 && envAttachCopy && ( +

+ {envAttachCopy.text}

)} {scopeCoverage.uncovered.length > 0 && ( diff --git a/src/utils/environmentStep.js b/src/utils/environmentStep.js index 62fb7e4..aa0faf2 100644 --- a/src/utils/environmentStep.js +++ b/src/utils/environmentStep.js @@ -130,6 +130,29 @@ export const toggleCell = (selections, itemId, platformId) => { return next; }; +/** + * Step-3 attach-count sentence for the community-procedures card. Pure so + * the page stays a dispatcher and so the off state can never claim an + * attach that buildAttachPlan will not perform: addenda ride the community + * trunk, and the bank toggle gates attachment. Returns null when there is + * nothing to say (bank off, nothing selected). + */ +export const environmentAttachCopy = (useBank, attachedCount, totalAvailable) => { + const noun = attachedCount === 1 ? 'check' : 'checks'; + if (!useBank) { + if (attachedCount === 0) return null; + return { + active: false, + text: `${attachedCount} platform ${noun} selected in the Environment step will not attach while community test procedures are off. Turn them back on to apply your selections.` + }; + } + const verb = attachedCount === 1 ? 'attaches' : 'attach'; + return { + active: true, + text: `${attachedCount} platform ${noun} from the Environment step ${verb} as addenda (${totalAvailable} available).` + }; +}; + /** * The create-time attach plan. For every scoped item, the offers its checked * cells attach (ALL offers of each checked platform — user cells are the only diff --git a/src/utils/environmentStep.test.js b/src/utils/environmentStep.test.js index e936894..63ad50d 100644 --- a/src/utils/environmentStep.test.js +++ b/src/utils/environmentStep.test.js @@ -16,7 +16,8 @@ import { columnTotal, countAttached, attachedCountForItem, - compareCsfOrder + compareCsfOrder, + environmentAttachCopy } from './environmentStep'; import { getPlatformProcedures } from './platformBank'; @@ -192,3 +193,40 @@ describe('buildAttachPlan', () => { ); }); }); + +describe('environmentAttachCopy', () => { + test('bank on, plural: byte-matches the shipped step-3 sentence', () => { + expect(environmentAttachCopy(true, 493, 493)).toEqual({ + active: true, + text: '493 platform checks from the Environment step attach as addenda (493 available).' + }); + }); + + test('bank on, singular: attaches/check agreement preserved', () => { + expect(environmentAttachCopy(true, 1, 12)).toEqual({ + active: true, + text: '1 platform check from the Environment step attaches as addenda (12 available).' + }); + }); + + test('bank on, zero attached: still an honest active sentence', () => { + expect(environmentAttachCopy(true, 0, 493).text).toBe( + '0 platform checks from the Environment step attach as addenda (493 available).' + ); + }); + + test('bank OFF with selections: inactive warning, never an attach claim', () => { + const copy = environmentAttachCopy(false, 493, 493); + expect(copy.active).toBe(false); + expect(copy.text).toContain('will not attach'); + expect(copy.text).not.toContain('attach as addenda'); + }); + + test('bank OFF, singular selection keeps noun agreement', () => { + expect(environmentAttachCopy(false, 1, 12).text).toContain('1 platform check selected'); + }); + + test('bank OFF with nothing selected renders nothing', () => { + expect(environmentAttachCopy(false, 0, 493)).toBeNull(); + }); +});