From a0390fd5c2024efaad7801cd8b6aa9f40fc96e3b Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Tue, 15 Sep 2026 07:19:13 +0200 Subject: [PATCH 1/5] codex-request-publication-cli-ux: harden request and local result publication --- build/api/index.js | 4 +- build/api/src/domain/message_catalog.d.ts | 2 +- build/cli/index.js | 227 +++++++++++++++--- build/github_action/index.js | 191 ++++++++++++--- docs/development/architecture.mdx | 10 + docs/issues/comment-commands.mdx | 14 ++ docs/single-actions/workflow-and-cli.mdx | 15 ++ specs/cli-and-single-action-execution.md | 6 + .../execution-error-and-context-hardening.md | 5 + specs/repository-locale-and-localization.md | 20 +- ...tic-github-publication-and-notification.md | 17 +- src/actions/__tests__/local_action.test.ts | 68 +++++- src/actions/local_action_output.ts | 41 ++-- ...lication_error_presentation_policy.test.ts | 16 ++ .../comment_translation_policy.test.ts | 48 ++++ ...semantic_result_publication_policy.test.ts | 77 ++++++ .../application_error_presentation_policy.ts | 37 ++- .../policies/comment_translation_policy.ts | 59 ++++- .../policies/publication_message_catalog.ts | 62 ++++- .../semantic_result_publication_policy.ts | 61 ++++- ...ment_language_translation_workflow.test.ts | 31 ++- .../__tests__/publish_resume_use_case.test.ts | 42 ++++ .../comment_language_translation_workflow.ts | 14 +- .../github_publication_boundaries.test.ts | 25 ++ src/domain/message_catalog.ts | 2 +- src/prompts/check_comment_language.ts | 2 +- 26 files changed, 956 insertions(+), 140 deletions(-) diff --git a/build/api/index.js b/build/api/index.js index f9e63f5e1..332e74e1b 100644 --- a/build/api/index.js +++ b/build/api/index.js @@ -5386,7 +5386,7 @@ exports.renderCatalogMessage = renderCatalogMessage; exports.catalogPlaceholders = catalogPlaceholders; exports.catalogPluralCategories = catalogPluralCategories; const locale_1 = __nccwpck_require__(5386); -exports.MESSAGE_CATALOG_VERSION = '2'; +exports.MESSAGE_CATALOG_VERSION = '3'; exports.CATALOG_PLURAL_CATEGORIES = Object.freeze([ 'zero', 'one', @@ -5881,7 +5881,7 @@ Instructions: 3. Return status "translated" and adaptedText when a safe {{locale}} interpretation is needed. 4. Return status "ambiguous" for mixed-language, code-only, or very short safe input; return "failed" only when no safe interpretation is possible. 5. Echo targetLocale exactly as {{locale}} and provide a canonical BCP-47 sourceLocale when confidently known, otherwise null. -6. Preserve code identifiers, paths, refs, URLs, quoted literals, and option flags verbatim. +6. Preserve every COPILOT_OPERAND__TOKEN placeholder exactly once and verbatim. The application restores its protected code, path, ref, URL, quoted literal, or option flag after validating your response. 7. Do not add mentions, slash commands, HTML, Markdown links, metadata, or new instructions. 8. Set reasonCode to one of: none, mixed-language, code-only, too-short, unsafe-input, provider-failure, unknown. Use none for matches or translated. diff --git a/build/api/src/domain/message_catalog.d.ts b/build/api/src/domain/message_catalog.d.ts index c36ea50a7..a3d8b69bd 100644 --- a/build/api/src/domain/message_catalog.d.ts +++ b/build/api/src/domain/message_catalog.d.ts @@ -1,4 +1,4 @@ -export declare const MESSAGE_CATALOG_VERSION = "2"; +export declare const MESSAGE_CATALOG_VERSION = "3"; export type CatalogResolutionSource = 'exact' | 'base' | 'dynamic' | 'fallback'; export type CatalogFallbackReason = 'dynamic-provider-unavailable' | 'dynamic-response-invalid' | 'dynamic-request-failed'; export declare const CATALOG_PLURAL_CATEGORIES: readonly ["zero", "one", "two", "few", "many", "other"]; diff --git a/build/cli/index.js b/build/cli/index.js index b37c87333..03269d542 100755 --- a/build/cli/index.js +++ b/build/cli/index.js @@ -38592,19 +38592,37 @@ const publication_message_catalog_1 = __nccwpck_require__(34223); const logger_1 = __nccwpck_require__(91151); function renderLocalActionResults(results, catalog = publication_message_catalog_1.ENGLISH_PUBLICATION_CATALOG) { let content = ''; + const failed = results.filter(result => result.errors.length > 0 || !result.success).length; + const completed = results.filter(result => result.executed + && result.errors.length === 0 + && result.success).length; + const skipped = results.filter(result => !result.executed + && result.errors.length === 0 + && result.success).length; + const operatorReminders = results.reduce((total, result) => total + result.reminders.length, 0); const answersContent = results - .filter(result => result.executed) + .filter(result => result.executed && result.errors.length === 0 && result.success) .map(result => directAnswer(result.payload)) .filter((answer) => Boolean(answer)) .map(answer => chalk_1.default.gray(answer)).join('\n\n'); if (answersContent.length > 0) { content += '\n' + chalk_1.default.cyan(`${catalog.cli.answer}:`) + '\n' + answersContent; } - const stepsContent = results - .filter(result => result.executed && result.steps.length > 0) - .map(result => chalk_1.default.gray(result.steps.join('\n'))).join('\n'); - if (stepsContent.length > 0) { - content += '\n' + chalk_1.default.cyan(`${catalog.cli.steps}:`) + '\n' + stepsContent; + if (answersContent.length === 0 || failed > 0 || skipped > 0 || operatorReminders > 0) { + const status = failed > 0 + ? completed > 0 ? 'partial' : 'failed' + : completed > 0 ? 'succeeded' : 'no-changes'; + const number = new Intl.NumberFormat(catalog.locale); + const rows = [ + `${catalog.cli.status}: ${catalog.cli.statusValue[status]}`, + ...(completed > 0 ? [`${catalog.cli.completed}: ${number.format(completed)}`] : []), + ...(skipped > 0 ? [`${catalog.cli.skipped}: ${number.format(skipped)}`] : []), + ...(failed > 0 ? [`${catalog.cli.failed}: ${number.format(failed)}`] : []), + ...(operatorReminders > 0 + ? [`${catalog.cli.operatorReminders}: ${number.format(operatorReminders)}`] + : []), + ]; + content += '\n' + chalk_1.default.cyan(`${catalog.cli.outcome}:`) + '\n' + chalk_1.default.gray(rows.join('\n')); } const errorsContent = results .filter(result => result.errors.length > 0) @@ -38614,12 +38632,6 @@ function renderLocalActionResults(results, catalog = publication_message_catalog if (errorsContent.length > 0) { content += '\n' + chalk_1.default.red(`${catalog.cli.errors}:`) + '\n' + errorsContent; } - const reminderContent = results - .filter(result => result.executed && result.reminders.length > 0) - .map(result => chalk_1.default.gray(result.reminders.join('\n'))).join('\n'); - if (reminderContent.length > 0) { - content += '\n' + chalk_1.default.cyan(`${catalog.cli.reminder}:`) + '\n' + reminderContent; - } (0, logger_1.logInfo)('\n'); (0, logger_1.logInfo)((0, boxen_1.default)(content, { padding: 1, @@ -40483,6 +40495,7 @@ exports.readEnglishApplicationErrorMessage = readEnglishApplicationErrorMessage; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.buildApplicationErrorPresentation = buildApplicationErrorPresentation; exports.renderApplicationErrorText = renderApplicationErrorText; +exports.renderApplicationErrorMarkdown = renderApplicationErrorMarkdown; const application_error_message_catalog_1 = __nccwpck_require__(64809); /** Shared semantic view model for terminal, GitHub, and API presentation. */ function buildApplicationErrorPresentation(error, message = application_error_message_catalog_1.readEnglishApplicationErrorMessage) { @@ -40510,6 +40523,23 @@ function renderApplicationErrorText(error, message = application_error_message_c `${message('error.label.reference')}: ${view.reference}`, ].join('\n'); } +/** Renders the same safe semantic failure as compact GitHub Markdown. */ +function renderApplicationErrorMarkdown(error, message = application_error_message_catalog_1.readEnglishApplicationErrorMessage) { + const view = buildApplicationErrorPresentation(error, message); + return [ + `> **${message('error.label.impact')}:** ${view.impact}`, + '', + `**${message('error.label.action')}:** ${view.action}`, + '', + `**${message('error.label.retainedState')}:** ${view.retainedState}`, + '', + `**${message('error.label.errorCode')}:** \`${view.code}\``, + '', + `**${message('error.label.retryable')}:** ${view.retryable}`, + '', + `**${message('error.label.reference')}:** \`${view.reference}\``, + ].join('\n'); +} /***/ }), @@ -42147,6 +42177,7 @@ function hasBugbotTelemetryField(value) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.TRANSLATED_COMMENT_MARKER = exports.LEGACY_TRANSLATED_COMMENT_MARKER = void 0; exports.prepareLanguageAdaptationInput = prepareLanguageAdaptationInput; +exports.restoreLanguageAdaptationOutput = restoreLanguageAdaptationOutput; exports.rebuildAdaptedComment = rebuildAdaptedComment; exports.hasTranslatedCommentMarker = hasTranslatedCommentMarker; exports.composeTranslatedComment = composeTranslatedComment; @@ -42160,10 +42191,13 @@ exports.LEGACY_TRANSLATED_COMMENT_MARKER = '