Skip to content

ci: repair reviewed successor branches #83

ci: repair reviewed successor branches

ci: repair reviewed successor branches #83

name: Rebuild malformed-settings successor
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: write
jobs:
rebuild:
if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'successor/malformed-settings-recovery-current'
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
with:
ref: successor/malformed-settings-recovery-current
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Rebuild closed-PR net diff on current dev
shell: bash
run: |
git config user.name 'twoimo'
git config user.email '32544727+twoimo@users.noreply.github.com'
git remote add upstream https://github.com/Yeachan-Heo/gajae-code.git || true
git fetch --no-tags upstream dev
git fetch --no-tags origin backup/malformed-settings-e5f15e86
git reset --hard upstream/dev
git diff --binary 96f64a8e22eacf496742c87b0be2a38f1821ba91..origin/backup/malformed-settings-e5f15e86 > /tmp/settings-successor.patch
git apply --3way --index /tmp/settings-successor.patch || true
- name: Resolve four current-dev overlaps
shell: bash
run: |
python3 - <<'PY'
from pathlib import Path
def choose_ours(text: str) -> str:
lines = text.splitlines(keepends=True)
out: list[str] = []
index = 0
while index < len(lines):
if not lines[index].startswith('<<<<<<< '):
out.append(lines[index])
index += 1
continue
index += 1
ours: list[str] = []
while index < len(lines) and not lines[index].startswith('======='):
ours.append(lines[index])
index += 1
if index >= len(lines):
raise SystemExit('unterminated conflict before separator')
index += 1
while index < len(lines) and not lines[index].startswith('>>>>>>> '):
index += 1
if index >= len(lines):
raise SystemExit('unterminated conflict after separator')
index += 1
out.extend(ours)
return ''.join(out)
capability_path = Path('packages/coding-agent/src/capability/index.ts')
capability = choose_ours(capability_path.read_text())
guard = '''function assertDisabledProvidersWritable(activeSettings: Settings): void {
\tif (activeSettings.canWriteDurableConfig()) return;

Check failure on line 68 in .github/workflows/rebuild-settings-successor.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/rebuild-settings-successor.yml

Invalid workflow file

You have an error in your yaml syntax on line 68
\tthrow new Error(
\t\t"Cannot change settings while config.yml has invalid YAML syntax. Repair config.yml and reload settings.",
\t);
}
'''
persist_anchor = 'function persistDisabledProviders(activeSettings: Settings, providers: ReadonlySet<string>): void {'
if 'function assertDisabledProvidersWritable(' not in capability:
if persist_anchor not in capability:
raise SystemExit('capability persist anchor missing')
capability = capability.replace(persist_anchor, guard + persist_anchor, 1)
provider_anchor = '): void {\n\tconst providers = new Set(activeSettings.get("disabledProviders"));'
if capability.count(provider_anchor) != 2:
raise SystemExit('capability provider mutation anchors changed')
capability = capability.replace(
provider_anchor,
'): void {\n\tassertDisabledProvidersWritable(activeSettings);\n\tconst providers = new Set(activeSettings.get("disabledProviders"));',
)
set_anchor = '): void {\n\tpersistDisabledProviders(activeSettings, new Set(providerIds));'
if set_anchor not in capability:
raise SystemExit('capability set mutation anchor missing')
capability = capability.replace(
set_anchor,
'): void {\n\tassertDisabledProvidersWritable(activeSettings);\n\tpersistDisabledProviders(activeSettings, new Set(providerIds));',
1,
)
capability_path.write_text(capability)
session_path = Path('packages/coding-agent/src/session/agent-session.ts')
session = choose_ours(session_path.read_text())
next_level_anchor = '\t\t\t\tthis.#thinkingLevel = nextThinkingLevel;'
next_level_guard = '''\t\t\t\tthis.#thinkingLevelMutationRevision++;
\t\t\t\tthis.#thinkingLevelLiveMutationRevision++;
\t\t\t\tthis.#pendingThinkingLevelControlSuccess = undefined;
\t\t\t\tthis.#pendingThinkingLevelControlFailure = undefined;
\t\t\t\tthis.#thinkingVisibilityLiveMutationRevision++;
\t\t\t\tthis.#pendingThinkingVisibilityControlSuccess = undefined;
\t\t\t\tthis.#pendingThinkingVisibilityControlFailure = undefined;
'''
if next_level_guard.strip() not in session:
if session.count(next_level_anchor) != 1:
raise SystemExit('switchSession next thinking anchor changed')
session = session.replace(next_level_anchor, next_level_guard + next_level_anchor, 1)
session = session.replace(
'\t\t\t\t\tthis.agent.setModel(previousModel);',
'\t\t\t\t\tthis.#setAgentModelWithReasoningContext(previousModel);',
1,
)
previous_level_anchor = '\t\t\t\tthis.#thinkingLevel = previousThinkingLevel;'
previous_level_guard = '''\t\t\t\tthis.#thinkingLevelMutationRevision++;
\t\t\t\tthis.#thinkingLevelLiveMutationRevision++;
\t\t\t\tthis.#pendingThinkingLevelControlSuccess = undefined;
\t\t\t\tthis.#pendingThinkingLevelControlFailure = undefined;
\t\t\t\tthis.#thinkingVisibilityLiveMutationRevision++;
\t\t\t\tthis.#pendingThinkingVisibilityControlSuccess = undefined;
\t\t\t\tthis.#pendingThinkingVisibilityControlFailure = undefined;
'''
if previous_level_guard.strip() not in session:
if session.count(previous_level_anchor) != 1:
raise SystemExit('switchSession rollback thinking anchor changed')
session = session.replace(previous_level_anchor, previous_level_guard + previous_level_anchor, 1)
session_path.write_text(session)
discovery_path = Path('packages/coding-agent/test/discovery/agent-discovery-disabled-providers.test.ts')
discovery = choose_ours(discovery_path.read_text())
discovery = discovery.replace(
'import { afterEach, beforeEach, describe, expect, test } from "bun:test";',
'import { afterEach, beforeEach, describe, expect, test, vi } from "bun:test";',
1,
)
discovery = discovery.replace(
'import { disableProvider, enableProvider } from "../../src/capability";',
'''import {
\tdisableProvider,
\tenableProvider,
\tgetDisabledProviders,
\tisProviderEnabled,
\tsetDisabledProviders,
} from "../../src/capability";''',
1,
)
read_only_test = '''\ttest("rejects provider toggles before mutating session settings when config is read-only", () => {
\t\tconst readOnlySettings = Settings.isolated({ disabledProviders: ["already-disabled"] });
\t\tconst canWrite = vi.spyOn(readOnlySettings, "canWriteDurableConfig").mockReturnValue(false);
\t\ttry {
\t\t\texpect(() => disableProvider("new-provider", readOnlySettings)).toThrow("Repair config.yml");
\t\t\texpect(isProviderEnabled("new-provider", readOnlySettings)).toBe(true);
\t\t\texpect(() => enableProvider("already-disabled", readOnlySettings)).toThrow("Repair config.yml");
\t\t\texpect(isProviderEnabled("already-disabled", readOnlySettings)).toBe(false);
\t\t\texpect(() => setDisabledProviders(["replacement"], readOnlySettings)).toThrow("Repair config.yml");
\t\t\texpect(getDisabledProviders(readOnlySettings)).toEqual(["already-disabled"]);
\t\t} finally {
\t\t\tcanWrite.mockRestore();
\t\t}
\t});
'''
if 'rejects provider toggles before mutating session settings when config is read-only' not in discovery:
end = discovery.rfind('\n});')
if end < 0:
raise SystemExit('discovery describe terminator missing')
discovery = discovery[:end] + '\n' + read_only_test + discovery[end:]
discovery_path.write_text(discovery)
input_path = Path('packages/coding-agent/test/input-controller-keybindings.test.ts')
input_test = choose_ours(input_path.read_text())
settings_import = 'import { resetSettingsForTest, Settings } from "../src/config/settings";\n'
if settings_import not in input_test:
path_import = 'import * as path from "node:path";\n'
if path_import not in input_test:
raise SystemExit('input-controller path import missing')
input_test = input_test.replace(path_import, path_import + settings_import, 1)
visibility_test = '''\tit("routes accepted thinking visibility changes through the session", async () => {
\t\tconst activeSettings = await Settings.init({ inMemory: true });
\t\tconst set = vi.spyOn(activeSettings, "set");
\t\tconst { InputController, ctx } = await createContext();
\t\tconst setThinkingVisibility = vi.fn();
\t\tconst session = ctx.session as unknown as { setThinkingVisibility: (visibility: "visible" | "hidden") => void };
\t\tsession.setThinkingVisibility = setThinkingVisibility;
\t\tctx.hideThinkingBlock = false;
\t\tctx.chatContainer = {
\t\t\tdetachChild: vi.fn(),
\t\t\taddChild: vi.fn(),
\t\t} as unknown as InteractiveModeContext["chatContainer"];
\t\tctx.rebuildChatFromMessages = vi.fn();
\t\ttry {
\t\t\tnew InputController(ctx).toggleThinkingBlockVisibility();
\t\t\texpect(set).toHaveBeenCalledWith("hideThinkingBlock", true);
\t\t\texpect(setThinkingVisibility).toHaveBeenCalledWith("hidden");
\t\t\texpect(set.mock.invocationCallOrder[0]).toBeLessThan(setThinkingVisibility.mock.invocationCallOrder[0]);
\t\t} finally {
\t\t\tset.mockRestore();
\t\t\tresetSettingsForTest();
\t\t}
\t});
'''
if 'routes accepted thinking visibility changes through the session' not in input_test:
marker = '\tit("registers the default IRC sidebar shortcut and consumes its dispatch", async () => {'
if marker not in input_test:
raise SystemExit('input-controller test insertion anchor missing')
input_test = input_test.replace(marker, visibility_test + marker, 1)
input_path.write_text(input_test)
for path in (capability_path, session_path, discovery_path, input_path):
text = path.read_text()
if '<<<<<<< ' in text or '=======' in text or '>>>>>>> ' in text:
raise SystemExit(f'unresolved conflict marker in {path}')
PY
git add \
packages/coding-agent/src/capability/index.ts \
packages/coding-agent/src/session/agent-session.ts \
packages/coding-agent/test/discovery/agent-discovery-disabled-providers.test.ts \
packages/coding-agent/test/input-controller-keybindings.test.ts
test -z "$(git diff --name-only --diff-filter=U)"
- uses: oven-sh/setup-bun@v2
with:
bun-version: '1.3.14'
- run: bun install --frozen-lockfile
- name: Format changed TypeScript files
shell: bash
run: |
mapfile -t changed < <(git diff --cached --name-only -- '*.ts' '*.tsx')
if [ ${#changed[@]} -gt 0 ]; then
bunx biome check --write "${changed[@]}"
git add "${changed[@]}"
fi
- name: Re-run exact prior blocker
run: bun test --timeout 30000 packages/coding-agent/test/modes/components/settings-selector-pet.test.ts
- name: Run malformed-settings control suite
run: |
bun test --timeout 30000 \
packages/coding-agent/test/notifications-config.test.ts \
packages/coding-agent/test/settings-manager.test.ts \
packages/coding-agent/test/issue-775-repro.test.ts \
packages/coding-agent/test/modes/components/settings-selector-memory-refresh.test.ts \
packages/coding-agent/test/modes/components/settings-selector-pet.test.ts \
packages/coding-agent/test/modes/components/thinking-selector.test.ts \
packages/coding-agent/test/startup-update-contract.test.ts \
packages/coding-agent/test/discovery/agent-discovery-disabled-providers.test.ts \
packages/coding-agent/test/input-controller-keybindings.test.ts
bun --cwd=packages/coding-agent run check
bun --cwd=packages/coding-agent run build
- name: Publish verified clean successor
shell: bash
run: |
git add -A
git commit -m 'fix(coding-agent): harden malformed settings recovery'
git push --force origin HEAD:successor/malformed-settings-recovery-current