From 3291b3a693bbd1ec2686fd9ea706fe71d1be87da Mon Sep 17 00:00:00 2001 From: fermatmind <17551046983@163.com> Date: Sun, 12 Jul 2026 17:50:59 +0800 Subject: [PATCH 1/3] fix(enneagram): normalize evidence refresh readback --- ...enneagram-en13-evidence-production-ops.yml | 91 +++++++++++++++---- .../Services/Cms/EnneagramCmsDraftWriter.php | 25 ++++- ...ityEnneagramEvidenceRefreshCommandTest.php | 59 +++++++++++- docs/codex/pr-train-state.json | 24 +++-- docs/codex/pr-train.yaml | 28 +++++- 5 files changed, 195 insertions(+), 32 deletions(-) diff --git a/.github/workflows/enneagram-en13-evidence-production-ops.yml b/.github/workflows/enneagram-en13-evidence-production-ops.yml index 400ac5c21..9c3c8377f 100644 --- a/.github/workflows/enneagram-en13-evidence-production-ops.yml +++ b/.github/workflows/enneagram-en13-evidence-production-ops.yml @@ -119,17 +119,49 @@ jobs: || ssh-keygen -F "[$DEPLOY_HOST]:$DEPLOY_PORT" -f ~/.ssh/known_hosts >/dev/null - name: Run mandatory dry-run then optional bounded refresh + id: refresh shell: bash run: | - set -euo pipefail + set -uo pipefail target="$DEPLOY_USER@$DEPLOY_HOST" for value in DEPLOY_PATH RELEASE_SHA OPERATION_MODE COHORT_SHA256 EN_PACKAGE EN_LEDGER ZH_PACKAGE ZH_LEDGER; do printf -v "q_${value,,}" '%q' "${!value}" done + set +e ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o ConnectTimeout=10 -p "$DEPLOY_PORT" "$target" \ "DEPLOY_PATH=$q_deploy_path RELEASE_SHA=$q_release_sha OPERATION_MODE=$q_operation_mode COHORT_SHA256=$q_cohort_sha256 EN_PACKAGE=$q_en_package EN_LEDGER=$q_en_ledger ZH_PACKAGE=$q_zh_package ZH_LEDGER=$q_zh_ledger bash -s" <<'REMOTE' > summary.json set -Eeuo pipefail + stage="bootstrap" + tmp_dir="$(mktemp -d /tmp/en13-evidence-refresh.XXXXXX)" + dry_run_summary=null + write_summary=null + readback_summary=null + emit_summary() { + rc=$? + trap - EXIT + status=failure + failed_stage="$stage" + if [ "$rc" -eq 0 ]; then + status=success + failed_stage="" + fi + jq -n \ + --arg schema_version "enneagram-en13-evidence-production-ops.v1" \ + --arg status "$status" \ + --arg release_sha "$RELEASE_SHA" \ + --arg operation_mode "$OPERATION_MODE" \ + --arg cohort_sha256 "${actual_cohort:-}" \ + --arg failed_stage "$failed_stage" \ + --argjson dry_run "$dry_run_summary" \ + --argjson write "$write_summary" \ + --argjson readback "$readback_summary" \ + '{schema_version:$schema_version,status:$status,release_sha:$release_sha,operation_mode:$operation_mode,cohort_sha256:$cohort_sha256,failed_stage:(if $failed_stage == "" then null else $failed_stage end),dry_run:$dry_run,write:$write,readback:$readback}' + rm -rf "$tmp_dir" + exit "$rc" + } + trap emit_summary EXIT cd "$DEPLOY_PATH/current" + stage="deployed_revision" deployed_sha="$(tr -d '\r\n' < REVISION)" test "$deployed_sha" = "$RELEASE_SHA" cd backend @@ -161,33 +193,56 @@ jobs: echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), PHP_EOL; ' } - run_refresh dry_run > /tmp/en13-evidence-dry-run.json - jq -e '.ok == true and (.status == "dry_run_ready" or .status == "already_refreshed") and .target_count == 26 and (.issues | length) == 0' /tmp/en13-evidence-dry-run.json >/dev/null - actual_cohort="$(jq -r '.cohort_sha256' /tmp/en13-evidence-dry-run.json)" + stage="dry_run_execution" + run_refresh dry_run > "$tmp_dir/dry-run.json" + dry_run_summary="$(jq -c '{ok,status,target_count,already_current_count,updated_count,issue_count:(.issues|length),negative_guarantees}' "$tmp_dir/dry-run.json")" + stage="dry_run_validation" + jq -e '.ok == true and (.status == "dry_run_ready" or .status == "already_refreshed") and .target_count == 26 and (.issues | length) == 0' "$tmp_dir/dry-run.json" >/dev/null + actual_cohort="$(jq -r '.cohort_sha256' "$tmp_dir/dry-run.json")" if [ "$OPERATION_MODE" = write ]; then + stage="cohort_validation" test "$actual_cohort" = "$COHORT_SHA256" token="ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01:${RELEASE_SHA}:${COHORT_SHA256}" - ENNEAGRAM_EN13_EVIDENCE_WRITE_ENABLED=true run_refresh write "$COHORT_SHA256" "$token" > /tmp/en13-evidence-write.json - jq -e '.ok == true and (.status == "refreshed" or .status == "already_refreshed") and .target_count == 26 and (.issues | length) == 0' /tmp/en13-evidence-write.json >/dev/null - run_refresh dry_run > /tmp/en13-evidence-readback.json - jq -e '.ok == true and .status == "already_refreshed" and .target_count == 26 and .already_current_count == 26 and (.issues | length) == 0' /tmp/en13-evidence-readback.json >/dev/null + stage="write_execution" + ENNEAGRAM_EN13_EVIDENCE_WRITE_ENABLED=true run_refresh write "$COHORT_SHA256" "$token" > "$tmp_dir/write.json" + write_summary="$(jq -c '{ok,status,target_count,already_current_count,updated_count,issue_count:(.issues|length),negative_guarantees}' "$tmp_dir/write.json")" + stage="write_validation" + jq -e '.ok == true and (.status == "refreshed" or .status == "already_refreshed") and .target_count == 26 and (.issues | length) == 0' "$tmp_dir/write.json" >/dev/null + stage="readback_execution" + run_refresh dry_run > "$tmp_dir/readback.json" + readback_summary="$(jq -c '{ok,status,target_count,already_current_count,updated_count,issue_count:(.issues|length),negative_guarantees}' "$tmp_dir/readback.json")" + stage="readback_validation" + jq -e '.ok == true and .status == "already_refreshed" and .target_count == 26 and .already_current_count == 26 and (.issues | length) == 0' "$tmp_dir/readback.json" >/dev/null fi - jq -n \ - --arg schema_version "enneagram-en13-evidence-production-ops.v1" \ - --arg release_sha "$RELEASE_SHA" --arg operation_mode "$OPERATION_MODE" --arg cohort_sha256 "$actual_cohort" \ - --slurpfile dry_run /tmp/en13-evidence-dry-run.json \ - --slurpfile write /tmp/en13-evidence-write.json 2>/dev/null \ - --slurpfile readback /tmp/en13-evidence-readback.json 2>/dev/null \ - '{schema_version:$schema_version,status:"success",release_sha:$release_sha,operation_mode:$operation_mode,cohort_sha256:$cohort_sha256,dry_run:($dry_run[0]|{ok,status,target_count,already_current_count,updated_count,issue_count:(.issues|length),negative_guarantees}),write:($write[0] // null),readback:($readback[0] // null)}' \ - || jq -n --arg schema_version "enneagram-en13-evidence-production-ops.v1" --arg release_sha "$RELEASE_SHA" --arg operation_mode "$OPERATION_MODE" --arg cohort_sha256 "$actual_cohort" --slurpfile dry_run /tmp/en13-evidence-dry-run.json \ - '{schema_version:$schema_version,status:"success",release_sha:$release_sha,operation_mode:$operation_mode,cohort_sha256:$cohort_sha256,dry_run:($dry_run[0]|{ok,status,target_count,already_current_count,updated_count,issue_count:(.issues|length),negative_guarantees}),write:null,readback:null}' - rm -f /tmp/en13-evidence-dry-run.json /tmp/en13-evidence-write.json /tmp/en13-evidence-readback.json + stage="completed" REMOTE + remote_exit=$? + set -e + if ! jq -e '.schema_version == "enneagram-en13-evidence-production-ops.v1" and (.status == "success" or .status == "failure")' summary.json >/dev/null 2>&1; then + jq -n \ + --arg release_sha "$RELEASE_SHA" \ + --arg operation_mode "$OPERATION_MODE" \ + '{schema_version:"enneagram-en13-evidence-production-ops.v1",status:"failure",release_sha:$release_sha,operation_mode:$operation_mode,cohort_sha256:"",failed_stage:"transport_or_summary",dry_run:null,write:null,readback:null}' \ + > summary.json + remote_exit=1 + fi + echo "remote_exit=$remote_exit" >> "$GITHUB_OUTPUT" - name: Upload sanitized aggregate summary + if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: enneagram-en13-evidence-production-ops-${{ github.run_id }} path: summary.json if-no-files-found: error retention-days: 14 + + - name: Enforce fail-closed refresh result + if: always() + shell: bash + env: + REMOTE_EXIT: ${{ steps.refresh.outputs.remote_exit }} + run: | + set -euo pipefail + test "$REMOTE_EXIT" = "0" + jq -e '.status == "success" and .failed_stage == null' summary.json >/dev/null diff --git a/backend/app/Services/Cms/EnneagramCmsDraftWriter.php b/backend/app/Services/Cms/EnneagramCmsDraftWriter.php index e89ec0eb2..ee76ed25c 100644 --- a/backend/app/Services/Cms/EnneagramCmsDraftWriter.php +++ b/backend/app/Services/Cms/EnneagramCmsDraftWriter.php @@ -296,7 +296,16 @@ private function evidenceRefreshStateIsSafe(PersonalityPublicContentAsset $asset private function evidenceRefreshMatches(PersonalityPublicContentAsset $asset, array $candidate): bool { foreach ($candidate as $field => $value) { - if ($asset->getAttribute($field) !== $value) { + $actual = $asset->getAttribute($field); + if (is_array($value)) { + if (! is_array($actual) || $this->canonicalEvidenceJsonValue($actual) !== $this->canonicalEvidenceJsonValue($value)) { + return false; + } + + continue; + } + + if ($actual !== $value) { return false; } } @@ -304,6 +313,20 @@ private function evidenceRefreshMatches(PersonalityPublicContentAsset $asset, ar return true; } + private function canonicalEvidenceJsonValue(mixed $value): mixed + { + if (! is_array($value)) { + return $value; + } + if (array_is_list($value)) { + return array_map(fn (mixed $item): mixed => $this->canonicalEvidenceJsonValue($item), $value); + } + + ksort($value, SORT_STRING); + + return array_map(fn (mixed $item): mixed => $this->canonicalEvidenceJsonValue($item), $value); + } + private function evidenceVisibleLength(mixed $value): int { return is_scalar($value) ? mb_strlen(trim(strip_tags((string) $value))) : 0; diff --git a/backend/tests/Feature/Console/PersonalityEnneagramEvidenceRefreshCommandTest.php b/backend/tests/Feature/Console/PersonalityEnneagramEvidenceRefreshCommandTest.php index f181f579c..46ec9c7f5 100644 --- a/backend/tests/Feature/Console/PersonalityEnneagramEvidenceRefreshCommandTest.php +++ b/backend/tests/Feature/Console/PersonalityEnneagramEvidenceRefreshCommandTest.php @@ -7,6 +7,7 @@ use App\Models\PersonalityPublicContentAsset; use App\Services\Cms\EnneagramCmsDraftWriter; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; use Tests\TestCase; @@ -78,6 +79,40 @@ public function test_write_fails_closed_without_process_gate_or_exact_cohort(): $this->assertNotSame('', $cohort); } + public function test_mysql_json_object_key_normalization_is_semantically_idempotent_while_scalars_remain_exact(): void + { + $this->seedPublishedAssets(); + $dryRun = $this->evaluate(); + $cohort = $dryRun['cohort_sha256']; + $token = 'ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01:'.self::DEPLOYED_SHA.':'.$cohort; + $write = $this->evaluate(true, $cohort, $token, true); + $this->assertSame('refreshed', $write['status']); + + foreach (PersonalityPublicContentAsset::query()->orderBy('id')->get() as $asset) { + DB::table('personality_public_content_assets')->where('id', $asset->id)->update([ + 'content_sections_json' => json_encode( + self::reverseAssociativeObjectKeys((array) $asset->content_sections_json), + JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + ), + 'evidence_notes_json' => json_encode( + self::reverseAssociativeObjectKeys((array) $asset->evidence_notes_json), + JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR, + ), + ]); + } + + $semanticReadback = $this->evaluate(); + $this->assertSame('already_refreshed', $semanticReadback['status']); + $this->assertSame(26, $semanticReadback['already_current_count']); + + DB::table('personality_public_content_assets')->where('id', 1)->update([ + 'source_hash' => str_repeat('f', 64), + ]); + $scalarMismatch = $this->evaluate(); + $this->assertSame('dry_run_ready', $scalarMismatch['status']); + $this->assertSame(25, $scalarMismatch['already_current_count']); + } + public function test_dry_run_rejects_partial_or_unsafe_production_cohort(): void { $this->seedPublishedAssets(); @@ -128,12 +163,18 @@ public function test_production_workflow_is_sha_cohort_bound_dry_run_first_and_h $this->assertStringContainsString('Approve ENNEAGRAM EN13 evidence refresh dry-run for SHA ${RELEASE_SHA}', $workflow); $this->assertStringContainsString('I explicitly approve ENNEAGRAM EN13 evidence refresh for SHA ${RELEASE_SHA} cohort ${COHORT_SHA256}.', $workflow); $this->assertStringContainsString('test "$deployed_sha" = "$RELEASE_SHA"', $workflow); - $this->assertStringContainsString('run_refresh dry_run > /tmp/en13-evidence-dry-run.json', $workflow); + $this->assertStringContainsString('tmp_dir="$(mktemp -d /tmp/en13-evidence-refresh.XXXXXX)"', $workflow); + $this->assertStringContainsString('trap emit_summary EXIT', $workflow); + $this->assertStringContainsString('run_refresh dry_run > "$tmp_dir/dry-run.json"', $workflow); $this->assertStringContainsString('ENNEAGRAM_EN13_EVIDENCE_WRITE_ENABLED=true run_refresh write', $workflow); $this->assertStringContainsString('.status == "already_refreshed"', $workflow); + $this->assertStringContainsString('failed_stage:"transport_or_summary"', $workflow); + $this->assertStringContainsString('if: always()', $workflow); + $this->assertStringContainsString('Enforce fail-closed refresh result', $workflow); + $this->assertStringNotContainsString('/tmp/en13-evidence-dry-run.json', $workflow); $this->assertLessThan( strpos($workflow, 'ENNEAGRAM_EN13_EVIDENCE_WRITE_ENABLED=true run_refresh write'), - strpos($workflow, 'run_refresh dry_run > /tmp/en13-evidence-dry-run.json') + strpos($workflow, 'run_refresh dry_run > "$tmp_dir/dry-run.json"') ); $this->assertStringNotContainsString('seo:warm-sitemap-source-cache', $workflow); $this->assertStringNotContainsString('IndexNow', $workflow); @@ -141,6 +182,20 @@ public function test_production_workflow_is_sha_cohort_bound_dry_run_first_and_h $this->assertStringNotContainsString('deploy production', $workflow); } + private static function reverseAssociativeObjectKeys(mixed $value): mixed + { + if (! is_array($value)) { + return $value; + } + if (array_is_list($value)) { + return array_map(self::reverseAssociativeObjectKeys(...), $value); + } + + krsort($value, SORT_STRING); + + return array_map(self::reverseAssociativeObjectKeys(...), $value); + } + private function seedPublishedAssets(): void { foreach (['en-package', 'zh-package'] as $key) { diff --git a/docs/codex/pr-train-state.json b/docs/codex/pr-train-state.json index da9226b55..d9801ae92 100644 --- a/docs/codex/pr-train-state.json +++ b/docs/codex/pr-train-state.json @@ -88703,19 +88703,27 @@ }, "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01": { "id": "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01", "repo": "fap-api", "title": "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01: upgrade EN13 evidence provenance for llms-full", "base": "main", "branch": "codex/enneagram-en13-evidence-provenance-upgrade-01", "depends_on": ["ENNEAGRAM-LLMS-TXT-RELEASE-GATE-01"], - "status": "local_checks_passed", "commit_sha": null, "pr_url": null, "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, + "status": "merged", "commit_sha": "6d264e37328df8e9612a34b8772646b24cc89c7f", "pr_url": "https://github.com/fermatmind/fap-api/pull/3016", "failure_reason": null, "merged_at": "2026-07-12T07:43:13Z", "remote_branch_deleted": true, "local_cleanup_executed": true, "production_write_execution": false, "database_mutation": false, "cms_mutation": false, "search_channel_action": false, "llms_release_execution": false, "deploy_allowed": false, - "checks": {"authorization": {"status": "pass", "evidence": "User authorized the dependency-loop repair, bilingual 26-page evidence upgrade, scoped production-ops implementation, and manifest/state synchronization; deployment and all production writes remain separately gated."}, "dependency": {"status": "pass", "evidence": "ENNEAGRAM-LLMS-TXT-RELEASE-GATE-01 is merged as PR #3013 and deployed within production SHA ba4cfbb04d44a9f3541a6a686f4daa0d0f232d4a."}, "source_validation": {"status": "pass", "evidence": "PubMed PMID 33332604 / DOI 10.1002/jclp.23097 verifies mixed reliability/validity evidence and limited support for secondary propositions; all page copy explicitly limits this to a cautious evidence boundary."}, "local_validation": {"status": "pass", "evidence": "26 bilingual pages contain 26 visible evidence sections and 52 traceable notes using two stable source IDs with independently localized claims/limitations. 17 focused refresh/draft/llms tests passed with 846 assertions; publish regressions passed separately; Pint, actionlint, YAML/JSON, generator idempotency, bilingual parity, runtime-freeze classifier, scope, and git diff checks passed. The complete backend scripts/ci_verify_mbti.sh chain passed on the final runtime structure."}}, - "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": false, "post_merge_revalidated": false}, - "transitions": [{"at": "2026-07-11T19:03:22Z", "from": "unregistered", "to": "pending_dependency", "note": "Initialized for 26 EN13 bilingual evidence/provenance repair without production mutation."}, {"at": "2026-07-12T07:00:00Z", "from": "pending_dependency", "to": "in_progress", "note": "User authorized correcting the llms train dependency loop and expanding scope to both EN13 locales plus a non-deploy production Ops gate. Production dry-run 29183297832 proved all 26 legacy EN13 pages lack stable source IDs while method boundaries pass; no CMS, llms, cache, Search, or deploy action is authorized in this PR."}, {"at": "2026-07-12T07:28:00Z", "from": "in_progress", "to": "local_checks_passed", "note": "Final scoped implementation passes evidence/source/bilingual QA, exact-26 atomic refresh and idempotency tests, preservation and fail-closed tests, workflow contract checks, existing draft/llms/publish regressions, formatting/static validation, runtime-freeze classification, and the full backend CI chain. No production action was executed."}, {"at": "2026-07-12T08:02:00Z", "from": "local_checks_passed", "to": "github_checks_pending", "note": "Rebased without conflict onto latest origin/main f0393acf8, reran the focused 17-test/846-assertion suite and static/scope checks, pushed implementation commit 5b8921670c3f359c609946a3cc648062ca329d38, and opened PR #3016."}, {"at": "2026-07-12T08:08:00Z", "from": "github_checks_pending", "to": "ready_to_merge", "note": "All required CI and Code Scanning checks passed on d9b64b93645fe93b4e3be289761a9eac5e656ba9; branch protection, scope, changed files, and PR body were revalidated before the final ledger-only commit."}] + "checks": {"authorization": {"status": "pass", "evidence": "User authorized the dependency-loop repair, bilingual 26-page evidence upgrade, scoped production-ops implementation, and manifest/state synchronization; deployment and all production writes remain separately gated."}, "dependency": {"status": "pass", "evidence": "ENNEAGRAM-LLMS-TXT-RELEASE-GATE-01 is merged as PR #3013 and deployed within production SHA ba4cfbb04d44a9f3541a6a686f4daa0d0f232d4a."}, "source_validation": {"status": "pass", "evidence": "PubMed PMID 33332604 / DOI 10.1002/jclp.23097 verifies mixed reliability/validity evidence and limited support for secondary propositions; all page copy explicitly limits this to a cautious evidence boundary."}, "local_validation": {"status": "pass", "evidence": "26 bilingual pages contain 26 visible evidence sections and 52 traceable notes using two stable source IDs with independently localized claims/limitations. 17 focused refresh/draft/llms tests passed with 846 assertions; publish regressions passed separately; Pint, actionlint, YAML/JSON, generator idempotency, bilingual parity, runtime-freeze classifier, scope, and git diff checks passed. The complete backend scripts/ci_verify_mbti.sh chain passed on the final runtime structure."}, "github_required_checks": {"status": "pass", "evidence": "PR #3016 required CI and Code Scanning checks passed before squash merge."}, "post_merge": {"status": "pass", "evidence": "PR #3016 squash merge 6d264e37328df8e9612a34b8772646b24cc89c7f is contained by origin/main; local and remote task branches were cleaned."}}, + "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": true, "post_merge_revalidated": true}, + "transitions": [{"at": "2026-07-11T19:03:22Z", "from": "unregistered", "to": "pending_dependency", "note": "Initialized for 26 EN13 bilingual evidence/provenance repair without production mutation."}, {"at": "2026-07-12T07:00:00Z", "from": "pending_dependency", "to": "in_progress", "note": "User authorized correcting the llms train dependency loop and expanding scope to both EN13 locales plus a non-deploy production Ops gate. Production dry-run 29183297832 proved all 26 legacy EN13 pages lack stable source IDs while method boundaries pass; no CMS, llms, cache, Search, or deploy action is authorized in this PR."}, {"at": "2026-07-12T07:28:00Z", "from": "in_progress", "to": "local_checks_passed", "note": "Final scoped implementation passes evidence/source/bilingual QA, exact-26 atomic refresh and idempotency tests, preservation and fail-closed tests, workflow contract checks, existing draft/llms/publish regressions, formatting/static validation, runtime-freeze classification, and the full backend CI chain. No production action was executed."}, {"at": "2026-07-12T08:02:00Z", "from": "local_checks_passed", "to": "github_checks_pending", "note": "Rebased without conflict onto latest origin/main f0393acf8, reran the focused 17-test/846-assertion suite and static/scope checks, pushed implementation commit 5b8921670c3f359c609946a3cc648062ca329d38, and opened PR #3016."}, {"at": "2026-07-12T08:08:00Z", "from": "github_checks_pending", "to": "ready_to_merge", "note": "All required CI and Code Scanning checks passed on d9b64b93645fe93b4e3be289761a9eac5e656ba9; branch protection, scope, changed files, and PR body were revalidated before the final ledger-only commit."}, {"at": "2026-07-12T07:43:13Z", "from": "ready_to_merge", "to": "merged", "note": "PR #3016 squash-merged at 6d264e37328df8e9612a34b8772646b24cc89c7f; origin/main containment and local/remote branch cleanup were verified."}] }, "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01": { - "id": "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01", "repo": "fap-api production CMS Ops", "title": "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01: refresh exact 26-page EN13 evidence provenance", "base": "main", "branch": "codex/enneagram-en13-evidence-cms-refresh-01", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], - "status": "pending_dependency", "commit_sha": null, "pr_url": null, "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, + "id": "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01", "repo": "fap-api production CMS Ops", "title": "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01: refresh exact 26-page EN13 evidence provenance", "base": "main", "branch": "codex/enneagram-en13-evidence-cms-refresh-01", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01"], + "status": "blocked_readback_repair", "commit_sha": null, "pr_url": null, "failure_reason": "Production write run 29186611938 updated the exact 26 rows but failed its semantic readback because MySQL JSON object-key normalization was compared with strict PHP associative-array ordering; run 29186638542 also exposed stale temporary phase-summary reuse.", "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, "production_write_execution": true, "database_mutation": true, "cms_mutation": true, "search_channel_action": false, "llms_release_execution": false, "deploy_allowed": false, - "checks": {"authorization": {"status": "pending_exact_production_authorization", "evidence": "Manifest initialization is authorized. The evidence-upgrade merge SHA must first be separately deployed; then an exact 26-target dry-run must produce the cohort SHA before any independently authorized idempotent write."}}, + "checks": {"authorization": {"status": "pass_used", "evidence": "User authorized deployed SHA 6d264e37328df8e9612a34b8772646b24cc89c7f and cohort 98ff62847baddb6c2de43ed0e651e8995a95e0b423bc263f9568b1717c55f536 for the exact 26-row refresh."}, "production_dry_run": {"status": "pass", "evidence": "Run 29185954078 returned dry_run_ready for exactly 26 targets with zero issues and all negative guarantees."}, "production_write": {"status": "readback_repair_required", "evidence": "Run 29186611938 reported 26 refreshed rows before fail-closed readback; aggregate read-only inspection proved 26/26 expected package/hash/contract/evidence and 26/26 preserved release state, but the semantic idempotency gate did not pass."}}, "scope_validation": {"allowed_paths_only": false, "local_validation_passed": false, "github_checks_green": false, "post_merge_revalidated": false}, - "transitions": [{"at": "2026-07-12T07:00:00Z", "from": "unregistered", "to": "pending_dependency", "note": "Initialized as an exact 26-page evidence/provenance-only CMS refresh. Release state, robots, index/sitemap/llms eligibility, review state, Search, cache, and deployment remain immutable and separately gated."}] + "transitions": [{"at": "2026-07-12T07:00:00Z", "from": "unregistered", "to": "pending_dependency", "note": "Initialized as an exact 26-page evidence/provenance-only CMS refresh. Release state, robots, index/sitemap/llms eligibility, review state, Search, cache, and deployment remain immutable and separately gated."}, {"at": "2026-07-12T08:56:45Z", "from": "pending_dependency", "to": "blocked_readback_repair", "note": "Exact-SHA/cohort write updated 26/26 evidence rows and preserved every release-state invariant, but MySQL JSON object-key order caused strict readback mismatch and stale /tmp phase files polluted the subsequent dry-run artifact. No retry is allowed until the separately authorized repair merges and deploys."}] + }, + "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01": { + "id": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01", "repo": "fap-api", "title": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01: normalize evidence refresh readback", "base": "main", "branch": "codex/enneagram-en13-evidence-refresh-readback-repair-01", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], + "status": "local_checks_passed", "commit_sha": null, "pr_url": null, "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, + "production_write_execution": false, "database_mutation": false, "cms_mutation": false, "search_channel_action": false, "llms_release_execution": false, "deploy_allowed": false, + "checks": {"authorization": {"status": "pass", "evidence": "User explicitly authorized this exact five-file repair scope and manifest/state initialization; deployment and all production actions remain prohibited."}, "dependency": {"status": "pass", "evidence": "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01 is merged as PR #3016 at 6d264e37328df8e9612a34b8772646b24cc89c7f."}, "focused_local_validation": {"status": "pass", "evidence": "18 focused Enneagram evidence/draft/llms tests passed with 857 assertions; Pint, actionlint, YAML/JSON, semantic MySQL JSON key-order regression, exact scalar mismatch, scope, and git diff checks passed."}, "full_local_validation": {"status": "pass", "evidence": "After independent contract-infrastructure PR #3019 merged at 0683f5e284f08a175e2d9862790e3ca23e725957, the repair branch rebased onto latest origin/main and backend/scripts/ci_verify_mbti.sh completed successfully with exit 0."}}, + "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": false, "post_merge_revalidated": false}, + "transitions": [{"at": "2026-07-12T08:58:00Z", "from": "unregistered", "to": "in_progress", "note": "Initialized to repair semantic MySQL JSON readback and stale workflow phase-summary reuse only. No deployment, CMS write, llms release, cache warm, or Search action is authorized."}, {"at": "2026-07-12T09:26:00Z", "from": "in_progress", "to": "blocked_external_baseline", "note": "All scoped checks pass, but the full chain and exact origin/main CI independently fail on the merged PERSONALITY-WARMUP-01 historical branch-diff assertion outside the authorized five-file scope. No PR was opened."}, {"at": "2026-07-12T09:49:30Z", "from": "blocked_external_baseline", "to": "local_checks_passed", "note": "Independent single-file repair PR #3019 merged green; this branch rebased onto merge 0683f5e284f08a175e2d9862790e3ca23e725957. Focused 18-test/857-assertion suite and the complete backend/scripts/ci_verify_mbti.sh chain passed; exact five-file scope restored after generated Big Five compile outputs were discarded."}] }, "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02": { "id": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02", "repo": "fap-api", "title": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02: re-evaluate Enneagram llms-full evidence gate", "base": "main", "branch": "codex/enneagram-llms-full-release-readiness-02", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], diff --git a/docs/codex/pr-train.yaml b/docs/codex/pr-train.yaml index 3c9068e87..e4f30c5f9 100644 --- a/docs/codex/pr-train.yaml +++ b/docs/codex/pr-train.yaml @@ -41257,7 +41257,7 @@ prs: branch: codex/enneagram-en13-evidence-provenance-upgrade-01 title: "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01: upgrade EN13 evidence provenance for llms-full" train_scope: enneagram_en13_evidence_provenance_upgrade - status: in_progress + status: merged scope: - Repair the 26 EN13 bilingual local content assets with visible evidence, stable source IDs, limitations, and aligned bilingual evidence structure for a later llms-full gate. - Keep all assets non-llms-full until a separate readiness result and authorization. @@ -41273,13 +41273,35 @@ prs: deploy_allowed: false content_authority: CMS/backend + - id: ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01 + repo: fap-api + depends_on: [ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01] + branch: codex/enneagram-en13-evidence-refresh-readback-repair-01 + title: "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01: normalize evidence refresh readback" + train_scope: enneagram_en13_evidence_refresh_readback_repair + status: in_progress + scope: + - Compare evidence JSON fields by recursively normalized object-key semantics while preserving list order and exact scalar matching. + - Use run-local temporary files, emit and upload a sanitized phased summary on failure, and reject stale summary reuse before fail-closed validation. + allowed_paths: [.github/workflows/enneagram-en13-evidence-production-ops.yml, backend/app/Services/Cms/EnneagramCmsDraftWriter.php, backend/tests/Feature/Console/PersonalityEnneagramEvidenceRefreshCommandTest.php, docs/codex/pr-train.yaml, docs/codex/pr-train-state.json] + do_not: [Deploy, write production CMS/DB, change CMS content or eligibility, release llms, warm cache, submit Search, expose content bodies, private paths, secrets, or topology.] + validation: [focused Enneagram evidence refresh regression tests including MySQL JSON key order and exact-26 idempotency, Pint, actionlint and YAML/JSON checks, changed-file scope validation, git diff --check] + merge_policy: { github_checks_required: true, squash: true, auto_merge: true } + production_write_execution: false + database_mutation: false + cms_mutation: false + search_channel_action: false + llms_release_execution: false + deploy_allowed: false + content_authority: CMS/backend + - id: ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01 repo: fap-api production CMS Ops - depends_on: [ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01] + depends_on: [ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01] branch: codex/enneagram-en13-evidence-cms-refresh-01 title: "ENNEAGRAM-EN13-EVIDENCE-CMS-REFRESH-01: refresh exact 26-page EN13 evidence provenance" train_scope: enneagram_en13_evidence_cms_refresh - status: pending_dependency + status: blocked_readback_repair scope: - After the evidence upgrade merge SHA is separately deployed, run an exact 26-page read-only dry-run and derive an immutable cohort SHA. - Only after separate exact deployed-SHA and cohort-SHA authorization, idempotently refresh visible evidence, evidence provenance, source package/hash, and contract version while preserving all release and review state. From 2d629a843c95ced2b234380ec75534cfdfb7f578 Mon Sep 17 00:00:00 2001 From: fermatmind <17551046983@163.com> Date: Sun, 12 Jul 2026 17:51:46 +0800 Subject: [PATCH 2/3] chore(codex): record evidence repair PR --- docs/codex/pr-train-state.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/codex/pr-train-state.json b/docs/codex/pr-train-state.json index d9801ae92..fc538b713 100644 --- a/docs/codex/pr-train-state.json +++ b/docs/codex/pr-train-state.json @@ -88719,11 +88719,11 @@ }, "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01": { "id": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01", "repo": "fap-api", "title": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01: normalize evidence refresh readback", "base": "main", "branch": "codex/enneagram-en13-evidence-refresh-readback-repair-01", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], - "status": "local_checks_passed", "commit_sha": null, "pr_url": null, "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, + "status": "github_checks_pending", "commit_sha": "3291b3a69", "pr_url": "https://github.com/fermatmind/fap-api/pull/3020", "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, "production_write_execution": false, "database_mutation": false, "cms_mutation": false, "search_channel_action": false, "llms_release_execution": false, "deploy_allowed": false, "checks": {"authorization": {"status": "pass", "evidence": "User explicitly authorized this exact five-file repair scope and manifest/state initialization; deployment and all production actions remain prohibited."}, "dependency": {"status": "pass", "evidence": "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01 is merged as PR #3016 at 6d264e37328df8e9612a34b8772646b24cc89c7f."}, "focused_local_validation": {"status": "pass", "evidence": "18 focused Enneagram evidence/draft/llms tests passed with 857 assertions; Pint, actionlint, YAML/JSON, semantic MySQL JSON key-order regression, exact scalar mismatch, scope, and git diff checks passed."}, "full_local_validation": {"status": "pass", "evidence": "After independent contract-infrastructure PR #3019 merged at 0683f5e284f08a175e2d9862790e3ca23e725957, the repair branch rebased onto latest origin/main and backend/scripts/ci_verify_mbti.sh completed successfully with exit 0."}}, "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": false, "post_merge_revalidated": false}, - "transitions": [{"at": "2026-07-12T08:58:00Z", "from": "unregistered", "to": "in_progress", "note": "Initialized to repair semantic MySQL JSON readback and stale workflow phase-summary reuse only. No deployment, CMS write, llms release, cache warm, or Search action is authorized."}, {"at": "2026-07-12T09:26:00Z", "from": "in_progress", "to": "blocked_external_baseline", "note": "All scoped checks pass, but the full chain and exact origin/main CI independently fail on the merged PERSONALITY-WARMUP-01 historical branch-diff assertion outside the authorized five-file scope. No PR was opened."}, {"at": "2026-07-12T09:49:30Z", "from": "blocked_external_baseline", "to": "local_checks_passed", "note": "Independent single-file repair PR #3019 merged green; this branch rebased onto merge 0683f5e284f08a175e2d9862790e3ca23e725957. Focused 18-test/857-assertion suite and the complete backend/scripts/ci_verify_mbti.sh chain passed; exact five-file scope restored after generated Big Five compile outputs were discarded."}] + "transitions": [{"at": "2026-07-12T08:58:00Z", "from": "unregistered", "to": "in_progress", "note": "Initialized to repair semantic MySQL JSON readback and stale workflow phase-summary reuse only. No deployment, CMS write, llms release, cache warm, or Search action is authorized."}, {"at": "2026-07-12T09:26:00Z", "from": "in_progress", "to": "blocked_external_baseline", "note": "All scoped checks pass, but the full chain and exact origin/main CI independently fail on the merged PERSONALITY-WARMUP-01 historical branch-diff assertion outside the authorized five-file scope. No PR was opened."}, {"at": "2026-07-12T09:49:30Z", "from": "blocked_external_baseline", "to": "local_checks_passed", "note": "Independent single-file repair PR #3019 merged green; this branch rebased onto merge 0683f5e284f08a175e2d9862790e3ca23e725957. Focused 18-test/857-assertion suite and the complete backend/scripts/ci_verify_mbti.sh chain passed; exact five-file scope restored after generated Big Five compile outputs were discarded."}, {"at": "2026-07-12T09:51:00Z", "from": "local_checks_passed", "to": "github_checks_pending", "note": "Committed scoped repair as 3291b3a69, pushed the task branch, and opened PR #3020. Awaiting all required GitHub checks."}] }, "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02": { "id": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02", "repo": "fap-api", "title": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02: re-evaluate Enneagram llms-full evidence gate", "base": "main", "branch": "codex/enneagram-llms-full-release-readiness-02", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], From cdaf34c490747d2e12118668e655f7c024292fef Mon Sep 17 00:00:00 2001 From: fermatmind <17551046983@163.com> Date: Sun, 12 Jul 2026 17:58:02 +0800 Subject: [PATCH 3/3] chore(codex): mark evidence repair merge ready --- docs/codex/pr-train-state.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/codex/pr-train-state.json b/docs/codex/pr-train-state.json index fc538b713..6301be1ee 100644 --- a/docs/codex/pr-train-state.json +++ b/docs/codex/pr-train-state.json @@ -88719,11 +88719,11 @@ }, "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01": { "id": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01", "repo": "fap-api", "title": "ENNEAGRAM-EN13-EVIDENCE-REFRESH-READBACK-REPAIR-01: normalize evidence refresh readback", "base": "main", "branch": "codex/enneagram-en13-evidence-refresh-readback-repair-01", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"], - "status": "github_checks_pending", "commit_sha": "3291b3a69", "pr_url": "https://github.com/fermatmind/fap-api/pull/3020", "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, + "status": "ready_to_merge", "commit_sha": "2d629a843", "pr_url": "https://github.com/fermatmind/fap-api/pull/3020", "failure_reason": null, "merged_at": null, "remote_branch_deleted": false, "local_cleanup_executed": false, "production_write_execution": false, "database_mutation": false, "cms_mutation": false, "search_channel_action": false, "llms_release_execution": false, "deploy_allowed": false, "checks": {"authorization": {"status": "pass", "evidence": "User explicitly authorized this exact five-file repair scope and manifest/state initialization; deployment and all production actions remain prohibited."}, "dependency": {"status": "pass", "evidence": "ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01 is merged as PR #3016 at 6d264e37328df8e9612a34b8772646b24cc89c7f."}, "focused_local_validation": {"status": "pass", "evidence": "18 focused Enneagram evidence/draft/llms tests passed with 857 assertions; Pint, actionlint, YAML/JSON, semantic MySQL JSON key-order regression, exact scalar mismatch, scope, and git diff checks passed."}, "full_local_validation": {"status": "pass", "evidence": "After independent contract-infrastructure PR #3019 merged at 0683f5e284f08a175e2d9862790e3ca23e725957, the repair branch rebased onto latest origin/main and backend/scripts/ci_verify_mbti.sh completed successfully with exit 0."}}, - "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": false, "post_merge_revalidated": false}, - "transitions": [{"at": "2026-07-12T08:58:00Z", "from": "unregistered", "to": "in_progress", "note": "Initialized to repair semantic MySQL JSON readback and stale workflow phase-summary reuse only. No deployment, CMS write, llms release, cache warm, or Search action is authorized."}, {"at": "2026-07-12T09:26:00Z", "from": "in_progress", "to": "blocked_external_baseline", "note": "All scoped checks pass, but the full chain and exact origin/main CI independently fail on the merged PERSONALITY-WARMUP-01 historical branch-diff assertion outside the authorized five-file scope. No PR was opened."}, {"at": "2026-07-12T09:49:30Z", "from": "blocked_external_baseline", "to": "local_checks_passed", "note": "Independent single-file repair PR #3019 merged green; this branch rebased onto merge 0683f5e284f08a175e2d9862790e3ca23e725957. Focused 18-test/857-assertion suite and the complete backend/scripts/ci_verify_mbti.sh chain passed; exact five-file scope restored after generated Big Five compile outputs were discarded."}, {"at": "2026-07-12T09:51:00Z", "from": "local_checks_passed", "to": "github_checks_pending", "note": "Committed scoped repair as 3291b3a69, pushed the task branch, and opened PR #3020. Awaiting all required GitHub checks."}] + "scope_validation": {"allowed_paths_only": true, "local_validation_passed": true, "github_checks_green": true, "post_merge_revalidated": false}, + "transitions": [{"at": "2026-07-12T08:58:00Z", "from": "unregistered", "to": "in_progress", "note": "Initialized to repair semantic MySQL JSON readback and stale workflow phase-summary reuse only. No deployment, CMS write, llms release, cache warm, or Search action is authorized."}, {"at": "2026-07-12T09:26:00Z", "from": "in_progress", "to": "blocked_external_baseline", "note": "All scoped checks pass, but the full chain and exact origin/main CI independently fail on the merged PERSONALITY-WARMUP-01 historical branch-diff assertion outside the authorized five-file scope. No PR was opened."}, {"at": "2026-07-12T09:49:30Z", "from": "blocked_external_baseline", "to": "local_checks_passed", "note": "Independent single-file repair PR #3019 merged green; this branch rebased onto merge 0683f5e284f08a175e2d9862790e3ca23e725957. Focused 18-test/857-assertion suite and the complete backend/scripts/ci_verify_mbti.sh chain passed; exact five-file scope restored after generated Big Five compile outputs were discarded."}, {"at": "2026-07-12T09:51:00Z", "from": "local_checks_passed", "to": "github_checks_pending", "note": "Committed scoped repair as 3291b3a69, pushed the task branch, and opened PR #3020. Awaiting all required GitHub checks."}, {"at": "2026-07-12T09:57:40Z", "from": "github_checks_pending", "to": "ready_to_merge", "note": "All nine GitHub checks passed, including verify-bigfive, verify-mbti-legacy, verify-mbti-v2, supply-chain, content-pack validation, hygiene, and code scanning. Scope and PR body revalidated."}] }, "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02": { "id": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02", "repo": "fap-api", "title": "ENNEAGRAM-LLMS-FULL-RELEASE-READINESS-02: re-evaluate Enneagram llms-full evidence gate", "base": "main", "branch": "codex/enneagram-llms-full-release-readiness-02", "depends_on": ["ENNEAGRAM-EN13-EVIDENCE-PROVENANCE-UPGRADE-01"],