Root-cause fix for main erosion (phantom merge): code of ORCH-067/069 reached
`done` while absent from origin/main (only their auto docs-PRs landed).
- FR-1: verify_merged_to_main confirms merge ONLY by `git merge-base
--is-ancestor <validated_sha> origin/main`; the OR-branch pr_already_merged is
removed (a merged PR no longer confirms). Empty SHA / git error -> False.
- FR-2: pr_already_merged demoted to merge_pr idempotency-guard; counts a PR only
when merged & head.ref==<branch> & base.ref=="main" (explicit in-loop filter).
- FR-3: merge_pr selects the open code-PR by head==<branch> AND base==main.
- FR-5: new deterministic check_main_regression in _handle_merge_verify (after
confirmed SHA-in-main, before done) verifies MAIN_REGRESSION_MARKERS still in
origin/main; deterministic count==0 -> alert "main regressed" + HOLD (NOT done,
no rollback); git error of the grep -> fail-open. Kill-switch
ORCH_REGRESSION_GUARD_ENABLED; non-self -> no-op.
- FR-4: root .gitattributes `CHANGELOG.md merge=union` so Unreleased edits
auto-merge on rebase without conflict (branch not rolled back).
Invariants unchanged (STAGE_TRANSITIONS, QG_CHECKS, deploy-status, merge-gate,
image-freshness, DB schema, external HTTP API); non-self repos no-op (INV-5);
never-raise (INV-1); merge only via Gitea PR-API (INV-2).
Docs: CHANGELOG, .env.example (README/ADR updated by architect). Tests:
tests/test_orch073_*.py (TC-01..18); existing merge-gate tests updated for the
new code-PR filter.
Refs: ORCH-073
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the hardcoded `len(name) > 80` cap in the QG-0 entry validation
(_qg0_errors) with a configurable Settings.qg0_title_max (env
ORCH_QG0_TITLE_MAX, default 200). The 80-char cap was a hygiene limit, not
structural, so valid 81-200 char titles were rejected without a business
reason. The limit is read dynamically per call and the error text interpolates
the active value.
Graceful degradation (AC-3, self-hosting safety): an empty/non-numeric env
value no longer crashes the process on startup. A field_validator(mode="before")
intercepts the raw env before int-parsing and falls back to 200 (never raises),
suppressing pydantic ValidationError.
Additive and backward-compatible (default 200 > old 80). Invariants unchanged:
STAGE_TRANSITIONS, QG_CHECKS registry, DB schema, slug [:30], lower limits,
soft-QG-0 warning path, API.
Refs: ORCH-069
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Закрывает P0/P1 ревью (attempt 2/3): документация = golden source.
- CHANGELOG.md: запись ORCH-067 в [Unreleased] (bump-дефолт, статус-строка
карточки по модели ORCH-066, кликабельный номер задачи, новые флаги).
- CLAUDE.md: раздел «Нотификации / Telegram live-tracker» (ТЗ §5).
- .env.example: ORCH_TRACKER_MODE=bump (синхрон с новым дефолтом) +
ORCH_TRACKER_LIVE_STATUS / _TTL_S / _TIMEOUT_S.
Refs: ORCH-067
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add CHANGELOG entry for the phantom-merge fix (merge-verify sub-gate,
deterministic merge actor, post-deploy verification, kill-switch).
Addresses P0 blocker from reviewer (attempt 2/3): docs = golden source
per CLAUDE.md §2/§6 and AC-5.
Refs: ORCH-071
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Staging check suite passed against orchestrator-staging (8501), exit 0.
All REAL pipeline checks green; sandbox-infra C9a/C9b waived per ORCH-061.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>