@7n/rules 1.49.23 → 1.49.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -31,7 +31,8 @@ LLM отримує лише bounded-завдання:
|
|
|
31
31
|
|
|
32
32
|
1. semantic triage кандидатів, які JS не може оцінити за Git-фактами;
|
|
33
33
|
2. розв'язання змістових конфліктів і перевірку перенесеної поведінки у вже
|
|
34
|
-
підготовленому worktree
|
|
34
|
+
підготовленому worktree;
|
|
35
|
+
3. бізнесовий та архітектурний опис PR за bounded-фактами фінального diff.
|
|
35
36
|
|
|
36
37
|
LLM не видаляє refs, не створює worktree, не push-ить і не відкриває PR.
|
|
37
38
|
LLM виконує лише narrow tests, потрібні під час правок; full repository tests,
|
|
@@ -55,6 +56,8 @@ heartbeat з elapsed time. Формат однаковий у TTY/CI, тому c
|
|
|
55
56
|
|
|
56
57
|
- triage — повноту verdicts, schema, groups і commit OID;
|
|
57
58
|
- worktree — відсутність conflict markers та `git diff --check`;
|
|
59
|
+
- PR description — JSON schema, evidence paths із реального diff і перевагу
|
|
60
|
+
business/architecture змісту над behavior/risk details;
|
|
58
61
|
- поведінку — repository test script відносно test baseline чистої
|
|
59
62
|
`origin/<baseBranch>` і changelog gate. Red baseline приймається лише для
|
|
60
63
|
розпізнаних Vitest failures, якщо після перенесення не додалось нових.
|
|
@@ -91,6 +94,13 @@ transport. Після провалу `max` джерело fail-closed лишає
|
|
|
91
94
|
- Перед push обов'язково проходять фінальний tree-diff guard, domain lint для
|
|
92
95
|
non-code paths, changelog і `git diff --check`; code changes додатково
|
|
93
96
|
проходять scoped docs/lint та tests.
|
|
97
|
+
- Перед push JS передає min-моделі bounded final diff, commit metadata,
|
|
98
|
+
triage rationale і behavioral verification. Модель повертає лише validated
|
|
99
|
+
JSON, а JS рендерить PR body із видимими секціями «Навіщо»,
|
|
100
|
+
«Бізнес-результат», «Архітектура», «Поведінка» та «Ризики та сумісність»;
|
|
101
|
+
source і evidence paths лишаються у collapsed technical details.
|
|
102
|
+
- Невалідний PR description повторюється на max; повторний провал fail-closed
|
|
103
|
+
зберігає worktree і не push-ить гілку з misleading описом.
|
|
94
104
|
- Canonical fixers охоплюють code і non-code directories; після механічного
|
|
95
105
|
виправлення фінальні gates обов'язково запускаються повторно без fix.
|
|
96
106
|
- Behavioral LLM не викликається для змін без code paths; test baseline
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: orchestrate.mjs
|
|
4
4
|
resource: npm/skills/git-reconcile/js/orchestrate.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: fc877b0d
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
@@ -20,13 +20,15 @@ docgen:
|
|
|
20
20
|
|
|
21
21
|
Для корисної групи JS створює ізольований worktree від policy base, застосовує commits або stash і відсікає порожній tree diff. `captureCachedBehaviorBaseline` кешує Promise baseline за base OID, щоб concurrent PR-групи не дублювали test run. `validateBehaviorState` перевіряє Git state, scoped docs/lint, tests відносно baseline і changelog. `remediateBehaviorState` запускає canonical fixers для code та non-code directories; після remediation final gates повторюються у read-only режимі.
|
|
22
22
|
|
|
23
|
+
Після фінальних gates `collectPullRequestFacts` збирає bounded final diff, commit metadata, changed paths, triage rationale та behavioral verification. `describePullRequest` викликає min-модель, а `validatePullRequestDescription` перевіряє JSON schema, factual evidence paths і перевагу business/architecture змісту; residual failure повторюється на max. `renderPullRequestBody` детерміновано ставить секції «Навіщо», «Бізнес-результат» та «Архітектура» перед поведінковими деталями, а source і evidence paths ховає в технічний `<details>`.
|
|
24
|
+
|
|
23
25
|
`runAsync` запускає install, tests, lint і PR checks без блокування event loop. Після push та `gh pr create` функція `verifyPullRequestReadiness` очікує terminal checks і передає набори PR/base checks у `classifyPullRequestChecks`. Якщо initial rollup порожній, orchestration дає GitHub коротке вікно на реєстрацію checks і повторює watch. Успішний outcome `pr-created` повертається лише для `ready`; regression, baseline-red, pending, timeout або unreadable checks зберігають branch, URL і worktree та блокують cleanup.
|
|
24
26
|
|
|
25
27
|
`commitPendingChanges` створює додатковий commit лише для staged remediation. Чистий index після перенесення branch-source є валідним, коли корисні commits уже присутні в `HEAD`. Порожній PR check rollup вважається непідтвердженим, а не успішним.
|
|
26
28
|
|
|
27
29
|
`hasOnlyChangeEntries` відсікає tree diff, у якому лишилися тільки `*/.changes/*.md`: такий результат вважається `patch-equivalent`, не створює PR і дозволяє cleanup source branch.
|
|
28
30
|
|
|
29
|
-
`pruneForensicDependencies` прибирає лише
|
|
31
|
+
`pruneForensicDependencies` прибирає лише відновлюваний `node_modules` зі збереженого forensic worktree. `cleanupSource` видаляє лише точний доказово безпечний branch/stash. Cleanup review-source дозволений після `drop` або коли всі його групи завершились `pr-created` чи `patch-equivalent`; `failed` і всі `pr-checks-*` outcomes лишають source та forensic worktree.
|
|
30
32
|
|
|
31
33
|
## Публічний API
|
|
32
34
|
|
|
@@ -34,12 +36,13 @@ docgen:
|
|
|
34
36
|
- `createPhaseProgress` — формує ANSI-free progress snapshots і heartbeat.
|
|
35
37
|
- `parseWorktrees`, `dedupeRefs`, `conflictFiles`, `inventoryRepository` — збирають і нормалізують Git inventory.
|
|
36
38
|
- `buildTriagePrompt`, `parseDecisionEnvelope`, `validateTriageOutcome` — задають і перевіряють bounded triage contract.
|
|
39
|
+
- `collectPullRequestFacts`, `validatePullRequestDescription`, `describePullRequest`, `renderPullRequestBody` — готують, перевіряють і формують business/architecture narrative фінального PR.
|
|
37
40
|
- `callRunner`, `callWithValidatedFallback` — викликають runner за схемою `min → validation → max`.
|
|
38
41
|
- `captureBehaviorBaseline`, `captureCachedBehaviorBaseline` — фіксують і кешують test baseline.
|
|
39
42
|
- `validateBehaviorState`, `validateFinalProjectGates`, `remediateBehaviorState` — виконують behavioral та canonical gates.
|
|
40
43
|
- `commitPendingChanges` — комітить лише staged remediation, не вимагаючи порожнього commit для вже перенесених commits.
|
|
41
44
|
- `hasOnlyChangeEntries` — розпізнає release-metadata-only diff, який не потребує PR.
|
|
42
|
-
- `pruneForensicDependencies` — звільняє
|
|
45
|
+
- `pruneForensicDependencies` — звільняє відновлювані dependencies без втрати Git evidence.
|
|
43
46
|
- `classifyPullRequestChecks`, `verifyPullRequestReadiness` — класифікують CI відносно base commit.
|
|
44
47
|
- `formatOutcomeCounts`, `formatReport` — формують точний deterministic summary.
|
|
45
48
|
- `cleanupSource`, `runGitReconcileOrchestrator` — виконують безпечний cleanup і координують повний flow.
|
|
@@ -49,6 +52,8 @@ docgen:
|
|
|
49
52
|
- Довгі child processes не блокують event loop і progress heartbeat.
|
|
50
53
|
- Baseline test Promise дедуплікується між concurrent PR-групами одного base OID.
|
|
51
54
|
- Canonical fixers охоплюють code/non-code scope, після чого gates повторюються без fix.
|
|
55
|
+
- PR description спирається лише на bounded final facts, а evidence paths завжди належать реальному diff.
|
|
56
|
+
- Business/architecture narrative має не меншу вагу, ніж behavior/risk details; повторно невалідний опис блокує push.
|
|
52
57
|
- PR вважається створеним успішно лише після terminal green checks.
|
|
53
58
|
- Порожній PR check rollup fail-closed зберігає forensic worktree.
|
|
54
59
|
- Forensic worktree зберігає Git evidence без накопичення `node_modules`.
|
|
@@ -13,6 +13,7 @@ import { readGitPolicy } from '../../../scripts/lib/git-policy.mjs'
|
|
|
13
13
|
const LLM_TIERS = ['min', 'max']
|
|
14
14
|
const REVIEW_BATCH_SIZE = 10
|
|
15
15
|
const PROMPT_TEXT_LIMIT = 12_000
|
|
16
|
+
const PR_DIFF_TEXT_LIMIT = 24_000
|
|
16
17
|
const PROGRESS_HEARTBEAT_MS = 30_000
|
|
17
18
|
const PR_CHECK_TIMEOUT_MS = 15 * 60_000
|
|
18
19
|
const DEFAULT_PR_CONCURRENCY = 3
|
|
@@ -27,6 +28,12 @@ const RENAME_DELETE_CONFLICT_RE = /^CONFLICT \(rename\/delete\): .+? renamed to
|
|
|
27
28
|
const SOURCE_CODE_RE = /\.(?:js|mjs|ts|vue|rs|py)$/
|
|
28
29
|
const CHANGE_ENTRY_RE = /(^|\/)\.changes\/[^/]+\.md$/
|
|
29
30
|
const WHITESPACE_RE = /\s+/
|
|
31
|
+
const PR_DESCRIPTION_ARRAY_FIELDS = [
|
|
32
|
+
'businessOutcomes',
|
|
33
|
+
'architectureChanges',
|
|
34
|
+
'behaviorChanges',
|
|
35
|
+
'risksAndCompatibility'
|
|
36
|
+
]
|
|
30
37
|
const ACP_PROGRESS_ENV = 'N_LLM_ACP_PROGRESS'
|
|
31
38
|
const REF_INVENTORY_FORMAT = ['%(refname)', '%00', '%(object', 'name)', '%00', '%(committer', 'date:iso-strict)'].join(
|
|
32
39
|
''
|
|
@@ -767,6 +774,184 @@ export function validateTriageOutcome(outcome, candidates) {
|
|
|
767
774
|
return { ok: true, value: envelope }
|
|
768
775
|
}
|
|
769
776
|
|
|
777
|
+
/**
|
|
778
|
+
* Збирає bounded факти з фінального diff для grounded бізнесового й
|
|
779
|
+
* архітектурного опису PR без повторного repository exploration моделлю.
|
|
780
|
+
* @param {object} args контекст готового PR
|
|
781
|
+
* @returns {object} факти опису PR
|
|
782
|
+
*/
|
|
783
|
+
export function collectPullRequestFacts(args) {
|
|
784
|
+
const { cwd, baseRef, source, title, rationale = '', verification = '', spawnFn = spawnSync } = args
|
|
785
|
+
const range = `${baseRef}...HEAD`
|
|
786
|
+
const changedPaths = git(['diff', '--name-only', range], cwd, spawnFn).stdout.split('\n').filter(Boolean)
|
|
787
|
+
const diff = git(['diff', '--no-ext-diff', '--unified=2', range], cwd, spawnFn).stdout
|
|
788
|
+
return {
|
|
789
|
+
source,
|
|
790
|
+
title,
|
|
791
|
+
rationale,
|
|
792
|
+
verification,
|
|
793
|
+
baseRef,
|
|
794
|
+
commits: git(['log', '--format=%h%x09%s', range], cwd, spawnFn).stdout.split('\n').filter(Boolean),
|
|
795
|
+
changedPaths,
|
|
796
|
+
diffStat: git(['diff', '--stat', range], cwd, spawnFn).stdout.trim(),
|
|
797
|
+
diff: diff.length > PR_DIFF_TEXT_LIMIT ? `${diff.slice(0, PR_DIFF_TEXT_LIMIT)}\n[diff truncated by JS]` : diff
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Формує bounded prompt, який забороняє implementation-changelog і вимагає
|
|
803
|
+
* business/architecture narrative лише з підготовлених JS-фактів.
|
|
804
|
+
* @param {object} facts фінальні Git та behavioral факти
|
|
805
|
+
* @returns {string} промпт
|
|
806
|
+
*/
|
|
807
|
+
export function buildPullRequestDescriptionPrompt(facts) {
|
|
808
|
+
return [
|
|
809
|
+
'Ти формуєш лише зміст PR description за вже зібраними JS-фактами.',
|
|
810
|
+
'Не запускай команди, не редагуй файли, не створюй PR і не вигадуй відсутній контекст.',
|
|
811
|
+
'Поясни передусім: навіщо зміна потрібна, який дає продуктово-операційний результат, як змінює responsibilities, boundaries, contracts або data/control flow.',
|
|
812
|
+
'Не переказуй diff по функціях і рядках. Називай implementation detail лише коли він є важливим architecture contract.',
|
|
813
|
+
'Не вигадуй клієнтів, фінансові метрики, deployment status або гарантії. Якщо бізнес-контекст обмежений, чесно сформулюй підтверджений operational/developer outcome.',
|
|
814
|
+
'Кожне твердження обґрунтуй facts; evidencePaths мають бути точними changedPaths.',
|
|
815
|
+
'Business context разом із businessOutcomes та architectureChanges має бути не коротшим за behaviorChanges разом із risksAndCompatibility.',
|
|
816
|
+
'Поверни лише JSON object без markdown:',
|
|
817
|
+
'{"businessContext":"...","businessOutcomes":["..."],"architectureChanges":["..."],"behaviorChanges":["..."],"risksAndCompatibility":["..."],"evidencePaths":["path/from/changedPaths"]}',
|
|
818
|
+
JSON.stringify(facts)
|
|
819
|
+
].join('\n\n')
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Перевіряє структуру, factual anchors і перевагу business/architecture
|
|
824
|
+
* змісту перед дрібними деталями реалізації.
|
|
825
|
+
* @param {{text:string}} outcome LLM output
|
|
826
|
+
* @param {{changedPaths:string[]}} facts фінальні Git-факти
|
|
827
|
+
* @returns {{ok:boolean,error?:string,value?:object}} validation
|
|
828
|
+
*/
|
|
829
|
+
export function validatePullRequestDescription(outcome, facts) {
|
|
830
|
+
const description = parseDecisionEnvelope(outcome.text)
|
|
831
|
+
if (!description) return { ok: false, error: 'відсутній JSON object опису PR' }
|
|
832
|
+
if (
|
|
833
|
+
typeof description.businessContext !== 'string' ||
|
|
834
|
+
description.businessContext.trim().length < 40 ||
|
|
835
|
+
description.businessContext.length > 1200 ||
|
|
836
|
+
description.businessContext.includes('\n')
|
|
837
|
+
) {
|
|
838
|
+
return { ok: false, error: 'businessContext має бути змістовним однорядковим текстом' }
|
|
839
|
+
}
|
|
840
|
+
for (const field of PR_DESCRIPTION_ARRAY_FIELDS) {
|
|
841
|
+
const values = description[field]
|
|
842
|
+
if (
|
|
843
|
+
!Array.isArray(values) ||
|
|
844
|
+
values.length === 0 ||
|
|
845
|
+
values.length > 5 ||
|
|
846
|
+
values.some(
|
|
847
|
+
value => typeof value !== 'string' || value.trim().length < 15 || value.length > 600 || value.includes('\n')
|
|
848
|
+
)
|
|
849
|
+
) {
|
|
850
|
+
return { ok: false, error: `${field} має містити 1-5 змістовних однорядкових пунктів` }
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const changedPaths = new Set(facts.changedPaths)
|
|
854
|
+
if (
|
|
855
|
+
!Array.isArray(description.evidencePaths) ||
|
|
856
|
+
description.evidencePaths.length === 0 ||
|
|
857
|
+
description.evidencePaths.length > 12 ||
|
|
858
|
+
description.evidencePaths.some(path => typeof path !== 'string' || !changedPaths.has(path))
|
|
859
|
+
) {
|
|
860
|
+
return { ok: false, error: 'evidencePaths мають бути непорожнім subset фактичних changedPaths' }
|
|
861
|
+
}
|
|
862
|
+
const focusedLength =
|
|
863
|
+
description.businessContext.length +
|
|
864
|
+
description.businessOutcomes.join('').length +
|
|
865
|
+
description.architectureChanges.join('').length
|
|
866
|
+
const detailLength = description.behaviorChanges.join('').length + description.risksAndCompatibility.join('').length
|
|
867
|
+
if (focusedLength < detailLength) {
|
|
868
|
+
return { ok: false, error: 'business та architecture зміст коротший за behavior/risk details' }
|
|
869
|
+
}
|
|
870
|
+
return { ok: true, value: description }
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Форматує validated narrative items як Markdown bullets.
|
|
875
|
+
* @param {string[]} values пункти секції
|
|
876
|
+
* @returns {string} Markdown
|
|
877
|
+
*/
|
|
878
|
+
function markdownBullets(values) {
|
|
879
|
+
return values.map(value => `- ${value.trim()}`).join('\n')
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Рендерить стабільний PR body: business та architecture секції видимі
|
|
884
|
+
* першими, а source/evidence залишаються у forensic details.
|
|
885
|
+
* @param {object} args validated description та deterministic facts
|
|
886
|
+
* @param {object} args.description validated narrative
|
|
887
|
+
* @param {object} args.facts deterministic Git facts
|
|
888
|
+
* @returns {string} Markdown PR body
|
|
889
|
+
*/
|
|
890
|
+
export function renderPullRequestBody({ description, facts }) {
|
|
891
|
+
const inline = value => String(value).trim().split(WHITESPACE_RE).join(' ')
|
|
892
|
+
const evidence = description.evidencePaths.map(path => `- \`${path.replaceAll('`', '\\`')}\``).join('\n')
|
|
893
|
+
return [
|
|
894
|
+
'## Навіщо',
|
|
895
|
+
'',
|
|
896
|
+
description.businessContext.trim(),
|
|
897
|
+
'',
|
|
898
|
+
'## Бізнес-результат',
|
|
899
|
+
'',
|
|
900
|
+
markdownBullets(description.businessOutcomes),
|
|
901
|
+
'',
|
|
902
|
+
'## Архітектура',
|
|
903
|
+
'',
|
|
904
|
+
markdownBullets(description.architectureChanges),
|
|
905
|
+
'',
|
|
906
|
+
'## Поведінка',
|
|
907
|
+
'',
|
|
908
|
+
markdownBullets(description.behaviorChanges),
|
|
909
|
+
'',
|
|
910
|
+
'## Ризики та сумісність',
|
|
911
|
+
'',
|
|
912
|
+
markdownBullets(description.risksAndCompatibility),
|
|
913
|
+
'',
|
|
914
|
+
'## Перевірки',
|
|
915
|
+
'',
|
|
916
|
+
`- \`git diff --check ${facts.baseRef}...HEAD\``,
|
|
917
|
+
'- scoped code lint/tests та domain lint для non-code paths',
|
|
918
|
+
'- `npx @7n/rules lint changelog --no-fix`',
|
|
919
|
+
...(facts.verification ? [`- LLM behavioral verification: ${inline(facts.verification).slice(0, 1000)}`] : []),
|
|
920
|
+
'',
|
|
921
|
+
'<details>',
|
|
922
|
+
'<summary>Технічні докази перенесення</summary>',
|
|
923
|
+
'',
|
|
924
|
+
`Джерело: \`${facts.source.replaceAll('`', '\\`')}\`.`,
|
|
925
|
+
'',
|
|
926
|
+
evidence,
|
|
927
|
+
'',
|
|
928
|
+
'</details>'
|
|
929
|
+
].join('\n')
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Генерує validated PR narrative через min→validation→max над фінальним diff.
|
|
934
|
+
* @param {object} args контекст готового worktree
|
|
935
|
+
* @returns {Promise<string>} deterministic Markdown body
|
|
936
|
+
*/
|
|
937
|
+
export async function describePullRequest(args) {
|
|
938
|
+
const { runner, cwd, baseRef, source, title, rationale, verification, deps, spawnFn, log, onProgress = noop } = args
|
|
939
|
+
const collectFacts = deps.collectPullRequestFacts ?? collectPullRequestFacts
|
|
940
|
+
const facts = collectFacts({ cwd, baseRef, source, title, rationale, verification, spawnFn })
|
|
941
|
+
const outcome = await callWithValidatedFallback({
|
|
942
|
+
runner,
|
|
943
|
+
prompt: buildPullRequestDescriptionPrompt(facts),
|
|
944
|
+
cwd,
|
|
945
|
+
deps,
|
|
946
|
+
log,
|
|
947
|
+
label: `PR description ${source}`,
|
|
948
|
+
onAttempt: ({ tier }) => onProgress('PR description', tier),
|
|
949
|
+
validate: result => validatePullRequestDescription(result, facts)
|
|
950
|
+
})
|
|
951
|
+
if (!outcome.ok) throw new Error(`LLM PR description: ${outcome.error}`)
|
|
952
|
+
return renderPullRequestBody({ description: outcome.validation.value, facts })
|
|
953
|
+
}
|
|
954
|
+
|
|
770
955
|
/**
|
|
771
956
|
* Перетворює довільний title/ref на branch slug.
|
|
772
957
|
* @param {string} value title/ref
|
|
@@ -1153,8 +1338,9 @@ export async function remediateBehaviorState(
|
|
|
1153
1338
|
if (validation.remediation !== 'canonical-fixers') return { attempted: false, ok: false }
|
|
1154
1339
|
|
|
1155
1340
|
const longRunner = resolveAsyncSpawn(spawnFn, asyncSpawnFn)
|
|
1156
|
-
const changedDirectories = [
|
|
1157
|
-
|
|
1341
|
+
const changedDirectories = [
|
|
1342
|
+
...new Set([...changedSourceDirectories(cwd, spawnFn), ...changedNonCodeDirectories(cwd, spawnFn)])
|
|
1343
|
+
].toSorted()
|
|
1158
1344
|
for (const path of changedDirectories) {
|
|
1159
1345
|
onProgress(`deterministic fix (${path})`)
|
|
1160
1346
|
const fixed = await runAsync('npx', ['@7n/rules', 'lint', '--path', path], cwd, longRunner, {
|
|
@@ -1503,7 +1689,7 @@ export function classifyPullRequestChecks(prChecks, baseChecks) {
|
|
|
1503
1689
|
}
|
|
1504
1690
|
|
|
1505
1691
|
/**
|
|
1506
|
-
* Видаляє лише
|
|
1692
|
+
* Видаляє лише відновлювані dependencies зі збереженого forensic worktree.
|
|
1507
1693
|
* Git metadata, commits і tracked/untracked зміни не зачіпаються.
|
|
1508
1694
|
* @param {string} worktreeCwd шлях worktree
|
|
1509
1695
|
* @returns {boolean} чи було що прибрати
|
|
@@ -1531,13 +1717,9 @@ export async function verifyPullRequestReadiness(args) {
|
|
|
1531
1717
|
allowFailure: true,
|
|
1532
1718
|
timeoutMs: PR_CHECK_TIMEOUT_MS
|
|
1533
1719
|
})
|
|
1534
|
-
let view = await runAsync(
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
cwd,
|
|
1538
|
-
longRunner,
|
|
1539
|
-
{ allowFailure: true }
|
|
1540
|
-
)
|
|
1720
|
+
let view = await runAsync('gh', ['pr', 'view', url, '--json', 'statusCheckRollup,baseRefOid'], cwd, longRunner, {
|
|
1721
|
+
allowFailure: true
|
|
1722
|
+
})
|
|
1541
1723
|
if (view.status !== 0) {
|
|
1542
1724
|
return { status: 'pr-checks-unverified', error: `Не вдалося прочитати PR checks: ${view.stderr || view.error}` }
|
|
1543
1725
|
}
|
|
@@ -1548,13 +1730,9 @@ export async function verifyPullRequestReadiness(args) {
|
|
|
1548
1730
|
allowFailure: true,
|
|
1549
1731
|
timeoutMs: PR_CHECK_TIMEOUT_MS
|
|
1550
1732
|
})
|
|
1551
|
-
view = await runAsync(
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
cwd,
|
|
1555
|
-
longRunner,
|
|
1556
|
-
{ allowFailure: true }
|
|
1557
|
-
)
|
|
1733
|
+
view = await runAsync('gh', ['pr', 'view', url, '--json', 'statusCheckRollup,baseRefOid'], cwd, longRunner, {
|
|
1734
|
+
allowFailure: true
|
|
1735
|
+
})
|
|
1558
1736
|
if (view.status !== 0) {
|
|
1559
1737
|
return {
|
|
1560
1738
|
status: 'pr-checks-unverified',
|
|
@@ -1721,23 +1899,24 @@ async function createPullRequest(args) {
|
|
|
1721
1899
|
const baseRef = policyBaseRef(worktree.cwd)
|
|
1722
1900
|
const baseBranch = readGitPolicy(worktree.cwd).baseBranch
|
|
1723
1901
|
git(['diff', '--check', `${baseRef}...HEAD`], worktree.cwd, spawnFn)
|
|
1902
|
+
const describePr = deps.describePullRequest ?? describePullRequest
|
|
1903
|
+
const body = await describePr({
|
|
1904
|
+
runner,
|
|
1905
|
+
cwd: worktree.cwd,
|
|
1906
|
+
baseRef,
|
|
1907
|
+
source,
|
|
1908
|
+
title: group.title,
|
|
1909
|
+
rationale: group.rationale ?? candidate.rationale ?? '',
|
|
1910
|
+
verification,
|
|
1911
|
+
deps,
|
|
1912
|
+
spawnFn,
|
|
1913
|
+
log,
|
|
1914
|
+
onProgress
|
|
1915
|
+
})
|
|
1724
1916
|
onProgress('push')
|
|
1725
1917
|
git(['push', '-u', 'origin', worktree.branch], worktree.cwd, spawnFn)
|
|
1726
1918
|
|
|
1727
1919
|
onProgress('create PR')
|
|
1728
|
-
const body = [
|
|
1729
|
-
`Джерело: \`${source}\`.`,
|
|
1730
|
-
'',
|
|
1731
|
-
group.rationale ?? candidate.rationale ?? `Корисну поведінку перенесено на актуальний ${baseBranch}.`,
|
|
1732
|
-
'',
|
|
1733
|
-
'Перевірки:',
|
|
1734
|
-
`- \`git diff --check ${baseRef}...HEAD\``,
|
|
1735
|
-
'- scoped code lint/tests та domain lint для non-code paths',
|
|
1736
|
-
'- `npx @7n/rules lint changelog --no-fix`',
|
|
1737
|
-
verification ? `- LLM behavioral verification: ${verification.slice(0, 1000)}` : ''
|
|
1738
|
-
]
|
|
1739
|
-
.filter(Boolean)
|
|
1740
|
-
.join('\n')
|
|
1741
1920
|
createdPr = run(
|
|
1742
1921
|
'gh',
|
|
1743
1922
|
['pr', 'create', '--base', baseBranch, '--head', worktree.branch, '--title', group.title, '--body', body],
|
|
@@ -2268,12 +2447,7 @@ export async function runGitReconcileOrchestrator(options = {}) {
|
|
|
2268
2447
|
|
|
2269
2448
|
const report = formatReport({ inventory, results })
|
|
2270
2449
|
log(report)
|
|
2271
|
-
const blockingStatuses = new Set([
|
|
2272
|
-
'failed',
|
|
2273
|
-
'pr-checks-regressed',
|
|
2274
|
-
'pr-checks-baseline-red',
|
|
2275
|
-
'pr-checks-unverified'
|
|
2276
|
-
])
|
|
2450
|
+
const blockingStatuses = new Set(['failed', 'pr-checks-regressed', 'pr-checks-baseline-red', 'pr-checks-unverified'])
|
|
2277
2451
|
const ok = results.every(result => !blockingStatuses.has(result.status) && result.incomplete !== true)
|
|
2278
2452
|
return { ok, report, inventory, results }
|
|
2279
2453
|
}
|