Files
boss/docs/superpowers/plans/2026-03-27-wechat-native-ui-rollback.md
2026-03-27 01:47:28 +08:00

27 KiB
Raw Permalink Blame History

WeChat Native UI Rollback Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Restore the Android app to the previously approved WeChat-like UI and interaction model while keeping the current native Android architecture, Boss API integration, login recovery, and OTA pipeline.

Architecture: Keep BossApiClient, current activities, login restore, OTA delivery, and backend routes intact. Add a small pure-Java surface-mapping helper with unit tests so the “allowed” WeChat-style information density is explicit, then rework MainActivity, shared UI helpers, and the core activities to render simple list-driven surfaces and a chat-first conversation page. Advanced ops capability stays in the codebase but leaves the first-level UI.

Tech Stack: Android AppCompat, XML layouts, Java 21, Gradle 8, JUnit4, existing Boss APIs, adb, existing npm run apk:release / npm run aab:release / scripts/deploy-server.sh.


File Structure

New files

  • android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java
    • Pure-Java contract for root tabs, conversation rows, device rows, my-page menus, and project quick actions.
  • android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java
    • Unit tests for WeChat-style information trimming and tab/menu contract.
  • android/app/src/main/res/layout/activity_project_chat.xml
    • Chat-first project detail layout with lightweight header strip and bottom composer.
  • android/app/src/main/res/drawable/bg_list_row.xml
    • Flat white list-cell background with subtle divider feel.
  • android/app/src/main/res/drawable/bg_tab_active.xml
    • Active bottom-tab background.
  • android/app/src/main/res/drawable/bg_tab_inactive.xml
    • Inactive bottom-tab background.
  • android/app/src/main/res/drawable/bg_message_incoming.xml
    • Incoming message bubble background.
  • android/app/src/main/res/drawable/bg_message_outgoing.xml
    • Outgoing message bubble background.
  • scripts/verify-native-wechat-release.sh
    • Local verification wrapper for build, health checks, artifacts, and docs.

Modified files

  • android/app/src/main/java/com/hyzq/boss/BossUi.java
    • Shared list-row, avatar, unread badge, bubble, and chip builders.
  • android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java
    • Base screen support for alternate layouts and lighter headers.
  • android/app/src/main/java/com/hyzq/boss/MainActivity.java
    • Root shell, tab state, conversations list, devices list, my-page menu, and return behavior.
  • android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java
    • Chat-first surface with only 项目目标 / 版本记录 as lightweight actions.
  • android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java
    • Simpler device detail first screen.
  • android/app/src/main/java/com/hyzq/boss/SecurityActivity.java
    • WeChat-style simple list and session summary.
  • android/app/src/main/java/com/hyzq/boss/SettingsActivity.java
    • Lighter settings presentation.
  • android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java
    • Skill rows instead of stacked heavy cards.
  • android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java
    • Simpler account list presentation.
  • android/app/src/main/java/com/hyzq/boss/AboutActivity.java
    • Cleaner About/OTA surface with advanced entry placement.
  • android/app/src/main/res/layout/activity_main.xml
    • Root login and tab shell layout.
  • android/app/src/main/res/layout/activity_screen.xml
    • Standard secondary-page shell layout.
  • android/app/src/main/res/values/colors.xml
    • WeChat-like flat palette.
  • android/app/src/main/res/values/styles.xml
    • White window background, no dashboard gradient.
  • android/app/build.gradle
    • Version bump for the rollback release build.
  • README.md
    • New native UI direction and build status.
  • docs/architecture/current_runtime_and_deploy_status_cn.md
    • Runtime truth after rollback.
  • docs/architecture/ai_handoff_index_cn.md
    • Effective Android surface summary.
  • docs/architecture/repo_map_cn.md
    • New layout / helper file map if structure changes.

Task 1: Freeze the WeChat Surface Contract in Unit Tests

Files:

  • Create: android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java

  • Create: android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java

  • Test: android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java

  • Step 1: Write the failing test

package com.hyzq.boss;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import org.json.JSONObject;
import org.junit.Test;

import java.util.Arrays;

public class WechatSurfaceMapperTest {
    @Test
    public void toConversationRow_keepsOnlyWechatFields() throws Exception {
        JSONObject source = new JSONObject()
                .put("projectTitle", "Boss 移动控制台")
                .put("preview", "主 Agent 已回复")
                .put("latestReplyLabel", "昨天")
                .put("unreadCount", 3)
                .put("riskLevel", "urgent")
                .put("activeDeviceCount", 2);

        WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(source);

        assertEquals("Boss 移动控制台", row.title);
        assertEquals("主 Agent 已回复", row.preview);
        assertEquals("昨天", row.timeLabel);
        assertEquals(3, row.unreadCount);
        assertFalse(row.preview.contains("设备"));
    }

    @Test
    public void toDeviceRow_keepsOnlySimpleSubtitle() throws Exception {
        JSONObject source = new JSONObject()
                .put("name", "Mac Studio")
                .put("status", "online")
                .put("account", "17600003315")
                .put("quota5h", 68)
                .put("quota7d", 81);

        WechatSurfaceMapper.DeviceRow row = WechatSurfaceMapper.toDeviceRow(source);

        assertEquals("Mac Studio", row.title);
        assertEquals("在线 · 17600003315", row.subtitle);
    }

    @Test
    public void rootMeMenuTitles_matchApprovedSimpleMenu() {
        assertEquals(
                Arrays.asList("账号与安全", "AI 账号", "设置", "技能", "关于"),
                WechatSurfaceMapper.rootMeMenuTitles()
        );
    }

    @Test
    public void projectQuickActions_keepOnlyGoalsAndVersions() {
        assertEquals(
                Arrays.asList("项目目标", "版本记录"),
                WechatSurfaceMapper.projectQuickActions()
        );
    }
}
  • Step 2: Run test to verify it fails

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon

Expected: FAIL with cannot find symbol or ClassNotFoundException for WechatSurfaceMapper.

  • Step 3: Write minimal implementation
package com.hyzq.boss;

import org.json.JSONObject;

import java.util.Arrays;
import java.util.List;

public final class WechatSurfaceMapper {
    private WechatSurfaceMapper() {}

    public static final class ConversationRow {
        public final String title;
        public final String preview;
        public final String timeLabel;
        public final int unreadCount;

        public ConversationRow(String title, String preview, String timeLabel, int unreadCount) {
            this.title = title;
            this.preview = preview;
            this.timeLabel = timeLabel;
            this.unreadCount = unreadCount;
        }
    }

    public static final class DeviceRow {
        public final String title;
        public final String subtitle;

        public DeviceRow(String title, String subtitle) {
            this.title = title;
            this.subtitle = subtitle;
        }
    }

    public static ConversationRow toConversationRow(JSONObject item) {
        return new ConversationRow(
                item.optString("projectTitle", "未命名会话"),
                item.optString("preview", "暂无消息"),
                item.optString("latestReplyLabel", ""),
                item.optInt("unreadCount", 0)
        );
    }

    public static DeviceRow toDeviceRow(JSONObject item) {
        String status = "online".equals(item.optString("status")) ? "在线" : "离线";
        String account = item.optString("account", "");
        String subtitle = account.isEmpty() ? status : status + " · " + account;
        return new DeviceRow(item.optString("name", "未命名设备"), subtitle);
    }

    public static List<String> rootMeMenuTitles() {
        return Arrays.asList("账号与安全", "AI 账号", "设置", "技能", "关于");
    }

    public static List<String> projectQuickActions() {
        return Arrays.asList("项目目标", "版本记录");
    }
}
  • Step 4: Run test to verify it passes

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon

Expected: PASS, BUILD SUCCESSFUL.

  • Step 5: Commit
cd /Users/kris/code/boss
git add android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java
git commit -m "test: freeze wechat surface contract"

Task 2: Rebuild the Root Shell and Conversation List

Files:

  • Modify: android/app/src/main/java/com/hyzq/boss/BossUi.java

  • Modify: android/app/src/main/java/com/hyzq/boss/MainActivity.java

  • Modify: android/app/src/main/res/layout/activity_main.xml

  • Modify: android/app/src/main/res/layout/activity_screen.xml

  • Modify: android/app/src/main/res/values/colors.xml

  • Modify: android/app/src/main/res/values/styles.xml

  • Create: android/app/src/main/res/drawable/bg_list_row.xml

  • Create: android/app/src/main/res/drawable/bg_tab_active.xml

  • Create: android/app/src/main/res/drawable/bg_tab_inactive.xml

  • Modify: android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java

  • Step 1: Write the failing test

Extend WechatSurfaceMapperTest.java with root-shell expectations:

@Test
public void rootTabOrder_isWechatStyle() {
    assertEquals(Arrays.asList("会话", "设备", "我的"), WechatSurfaceMapper.rootTabLabels());
}

@Test
public void mainPage_doesNotExposeOpsEntry() {
    assertFalse(WechatSurfaceMapper.rootMeMenuTitles().contains("运维与修复"));
}
  • Step 2: Run test to verify it fails

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon

Expected: FAIL because rootTabLabels() does not exist.

  • Step 3: Write minimal implementation

Add the missing contract method:

public static List<String> rootTabLabels() {
    return Arrays.asList("会话", "设备", "我的");
}

Then wire the UI around that contract.

Update BossUi.java to add list-oriented builders:

public static LinearLayout buildListRow(
        Context context,
        String leadingText,
        String title,
        String subtitle,
        String trailingText,
        int unreadCount,
        @Nullable View.OnClickListener listener
) {
    LinearLayout row = new LinearLayout(context);
    row.setOrientation(LinearLayout.HORIZONTAL);
    row.setBackgroundResource(R.drawable.bg_list_row);
    row.setPadding(dp(context, 16), dp(context, 14), dp(context, 16), dp(context, 14));
    if (listener != null) row.setOnClickListener(listener);
    return row;
}

Update MainActivity.java so the root render paths use WechatSurfaceMapper instead of card-heavy metadata:

private void renderConversationsRoot() {
    screenContent.removeAllViews();
    if (conversationsData == null || conversationsData.length() == 0) {
        screenContent.addView(BossUi.buildEmptyCard(this, "当前没有会话数据。"));
        return;
    }

    for (int i = 0; i < conversationsData.length(); i++) {
        JSONObject item = conversationsData.optJSONObject(i);
        if (item == null) continue;
        WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
        String projectId = item.optString("projectId", "");
        screenContent.addView(BossUi.buildListRow(
                this,
                item.optString("avatar", item.optString("projectTitle", "会").substring(0, 1)),
                row.title,
                row.preview,
                row.timeLabel,
                row.unreadCount,
                v -> openProject(projectId, row.title)
        ));
    }
}

Update activity_main.xml / activity_screen.xml to remove dashboard framing:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/boss_surface">
<Button
    android:id="@+id/tab_conversations"
    android:background="@drawable/bg_tab_active"
    android:text="会话" />
  • Step 4: Run tests and compile verification

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android :app:compileDebugJavaWithJavac --no-daemon

Expected:

  • Unit tests PASS

  • Java compile PASS

  • Step 5: Commit

cd /Users/kris/code/boss
git add android/app/src/main/java/com/hyzq/boss/BossUi.java android/app/src/main/java/com/hyzq/boss/MainActivity.java android/app/src/main/res/layout/activity_main.xml android/app/src/main/res/layout/activity_screen.xml android/app/src/main/res/values/colors.xml android/app/src/main/res/values/styles.xml android/app/src/main/res/drawable/bg_list_row.xml android/app/src/main/res/drawable/bg_tab_active.xml android/app/src/main/res/drawable/bg_tab_inactive.xml android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java
git commit -m "feat: restore wechat-style root shell"

Task 3: Rebuild Project Detail into a Chat-First Surface

Files:

  • Modify: android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java

  • Modify: android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java

  • Modify: android/app/src/main/java/com/hyzq/boss/BossUi.java

  • Create: android/app/src/main/res/layout/activity_project_chat.xml

  • Create: android/app/src/main/res/drawable/bg_message_incoming.xml

  • Create: android/app/src/main/res/drawable/bg_message_outgoing.xml

  • Step 1: Write the failing test

Extend WechatSurfaceMapperTest.java:

@Test
public void projectPrimarySections_keepOnlyChatEssentials() {
    assertEquals(
            Arrays.asList("quick_actions", "messages", "composer"),
            WechatSurfaceMapper.projectPrimarySections()
    );
}
  • Step 2: Run test to verify it fails

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon

Expected: FAIL because the helper still allows the old action shape or the assertions are not yet satisfied.

  • Step 3: Write minimal implementation

Teach BossScreenActivity to allow an alternate layout:

protected int getLayoutResId() {
    return R.layout.activity_screen;
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(getLayoutResId());
    ...
}

Then make ProjectDetailActivity use a chat layout and strip the heavy cards:

@Override
protected int getLayoutResId() {
    return R.layout.activity_project_chat;
}

private void renderProject(JSONObject payload) {
    JSONObject project = payload.optJSONObject("project");
    JSONArray messages = project == null ? null : project.optJSONArray("messages");

    configureScreen(project == null ? "项目聊天" : project.optString("name", "项目聊天"), "");
    replaceContent();
    appendQuickActions("项目目标", v -> openGoals(), "版本记录", v -> openVersions());
    renderMessages(messages);
}

and add the missing helper contract:

public static List<String> projectPrimarySections() {
    return Arrays.asList("quick_actions", "messages", "composer");
}

Render message bubbles through BossUi instead of generic cards:

public static LinearLayout buildMessageBubble(
        Context context,
        boolean self,
        String sender,
        String body,
        String meta
) {
    LinearLayout bubble = new LinearLayout(context);
    bubble.setBackgroundResource(self ? R.drawable.bg_message_outgoing : R.drawable.bg_message_incoming);
    return bubble;
}

Do not render these old sections in the main chat surface:

  • 当前主控身份

  • 主 Agent 调度结论

  • 线程预算卡片

  • 实时 APP 日志

  • 媒体与转发说明

  • Step 4: Run tests and screen compile verification

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android :app:compileDebugJavaWithJavac --no-daemon
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android assembleDebug --no-daemon

Expected:

  • Unit tests PASS

  • Java compile PASS

  • Debug assemble PASS

  • Step 5: Commit

cd /Users/kris/code/boss
git add android/app/src/main/java/com/hyzq/boss/BossScreenActivity.java android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java android/app/src/main/java/com/hyzq/boss/BossUi.java android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java android/app/src/main/res/layout/activity_project_chat.xml android/app/src/main/res/drawable/bg_message_incoming.xml android/app/src/main/res/drawable/bg_message_outgoing.xml
git commit -m "feat: restore wechat-style project chat page"

Task 4: Simplify Devices and Me Surfaces, Demote Advanced Ops

Files:

  • Modify: android/app/src/main/java/com/hyzq/boss/MainActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/SecurityActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/SettingsActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/AboutActivity.java

  • Modify: android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java

  • Modify: android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java

  • Step 1: Write the failing test

Extend WechatSurfaceMapperTest.java:

@Test
public void rootMeMenuTitles_keepApprovedOrder() {
    assertEquals(
            Arrays.asList("账号与安全", "AI 账号", "设置", "技能", "关于"),
            WechatSurfaceMapper.rootMeMenuTitles()
    );
}

@Test
public void advancedEntryTitle_movesOpsOutOfMainMePage() {
    assertEquals("高级与调试", WechatSurfaceMapper.advancedEntryTitle());
}
  • Step 2: Run test to verify it fails

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon

Expected: FAIL until the mapper and page rendering fully stop leaking quota / ops content.

  • Step 3: Write minimal implementation

Use WechatSurfaceMapper rows in MainActivity.renderDevicesRoot():

private void renderDevicesRoot() {
    screenContent.removeAllViews();
    screenContent.addView(BossUi.buildMenuRow(this, "添加设备", "通过绑定码接入新设备", null, v -> {
        startActivity(new Intent(this, DeviceEnrollmentActivity.class));
    }));

    for (int i = 0; i < devicesData.length(); i++) {
        JSONObject item = devicesData.optJSONObject(i);
        if (item == null) continue;
        WechatSurfaceMapper.DeviceRow row = WechatSurfaceMapper.toDeviceRow(item);
        screenContent.addView(BossUi.buildListRow(
                this,
                item.optString("avatar", "设"),
                row.title,
                row.subtitle,
                "",
                0,
                v -> openDevice(item.optString("id"), row.title)
        ));
    }
}

Simplify renderMeRoot() to only approved rows:

screenContent.addView(BossUi.buildMenuRow(this, "账号与安全", "登录与会话", null, v -> startActivity(new Intent(this, SecurityActivity.class))));
screenContent.addView(BossUi.buildMenuRow(this, "AI 账号", "主 GPT / 备用 GPT / API 容灾", null, v -> startActivity(new Intent(this, AiAccountsActivity.class))));
screenContent.addView(BossUi.buildMenuRow(this, "设置", "默认首页与提醒行为", null, v -> startActivity(new Intent(this, SettingsActivity.class))));
screenContent.addView(BossUi.buildMenuRow(this, "技能", "当前设备 Skill 清单", null, v -> startActivity(new Intent(this, SkillInventoryActivity.class))));
screenContent.addView(BossUi.buildMenuRow(this, "关于", "版本与更新", null, v -> startActivity(new Intent(this, AboutActivity.class))));

Move advanced entry deeper by placing it under AboutActivity as a non-primary menu row:

Button advanced = BossUi.buildSecondaryButton(this, "高级与调试");
advanced.setOnClickListener(v -> startActivity(new Intent(this, OpsCenterActivity.class)));

and add the helper contract:

public static String advancedEntryTitle() {
    return "高级与调试";
}

DeviceDetailActivity should keep only:

  • simple device summary
  • 查看技能
  • 编辑

and stop rendering related thread cards / enrollment draft cards on the first screen.

  • Step 4: Run tests and full debug verification

Run:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon
JAVA_HOME=$(/usr/libexec/java_home) ./android/gradlew -p ./android assembleDebug --no-daemon
adb -s 8KE0219724012168 shell am start -W -n com.hyzq.boss/.MainActivity
adb -s 8KE0219724012168 shell pidof com.hyzq.boss

Expected:

  • Unit tests PASS

  • Debug assemble PASS

  • MainActivity starts

  • pidof returns a process id

  • Step 5: Commit

cd /Users/kris/code/boss
git add android/app/src/main/java/com/hyzq/boss/MainActivity.java android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java android/app/src/main/java/com/hyzq/boss/SecurityActivity.java android/app/src/main/java/com/hyzq/boss/SettingsActivity.java android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java android/app/src/main/java/com/hyzq/boss/AboutActivity.java android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java
git commit -m "feat: simplify device and me surfaces"

Task 5: Verification, Release Packaging, Deployment, and Docs

Files:

  • Create: scripts/verify-native-wechat-release.sh

  • Modify: android/app/build.gradle

  • Modify: README.md

  • Modify: docs/architecture/current_runtime_and_deploy_status_cn.md

  • Modify: docs/architecture/ai_handoff_index_cn.md

  • Modify: docs/architecture/repo_map_cn.md

  • Step 1: Write the failing release verification script

Create scripts/verify-native-wechat-release.sh:

#!/bin/zsh
set -euo pipefail

cd /Users/kris/code/boss
npm run lint
npm run build
curl -fsS http://127.0.0.1:3000/api/health >/dev/null
curl -fsS http://127.0.0.1:4317/health >/dev/null
test -f android/app/build/outputs/apk/release/boss-android-v2.1.2-release.apk
test -f android/app/build/outputs/bundle/release/boss-android-v2.1.2-release.aab
rg -q '微信式' README.md
rg -q '2.1.2' docs/architecture/current_runtime_and_deploy_status_cn.md
  • Step 2: Run it to verify it fails

Run:

cd /Users/kris/code/boss
zsh ./scripts/verify-native-wechat-release.sh

Expected: FAIL because the 2.1.2 artifacts and updated docs do not exist yet.

  • Step 3: Write minimal release implementation

Bump the Android version:

versionCode 9
versionName "2.1.2"

Update docs to state:

  • native UI has returned to WeChat-style interaction
  • root tabs are 会话 / 设备 / 我的
  • chat page keeps only 项目目标 / 版本记录

Then build and publish:

cd /Users/kris/code/boss
JAVA_HOME=$(/usr/libexec/java_home) npm run apk:release
JAVA_HOME=$(/usr/libexec/java_home) npm run aab:release
  • Step 4: Run release verification and deploy

Run:

cd /Users/kris/code/boss
zsh ./scripts/verify-native-wechat-release.sh
adb -s 8KE0219724012168 install -r /Users/kris/code/boss/android/app/build/outputs/apk/release/boss-android-v2.1.2-release.apk
adb -s 8KE0219724012168 shell am start -W -n com.hyzq.boss/.MainActivity
./scripts/deploy-server.sh
"$HOME/.codex/skills/boss-server-debug/scripts/server_ssh.sh" exec "curl -sS http://127.0.0.1:3000/api/health"
curl -sS https://boss.hyzq.net/api/health
curl -sS https://boss.hyzq.net/downloads/boss-android-latest.json
curl -sS https://boss.hyzq.net/downloads/boss-android-latest-aab.json

Expected:

  • Verification script PASS

  • APK install PASS

  • MainActivity launch PASS

  • remote /api/health PASS

  • public health PASS

  • public APK/AAB metadata show 2.1.2 / versionCode 9

  • Step 5: Commit and publish

cd /Users/kris/code/boss
git add scripts/verify-native-wechat-release.sh android/app/build.gradle README.md docs/architecture/current_runtime_and_deploy_status_cn.md docs/architecture/ai_handoff_index_cn.md docs/architecture/repo_map_cn.md public/downloads
git commit -m "feat: ship wechat-style native rollback release"
git push gitea HEAD:refs/heads/codex/native-boss-android-2-1-0

Self-Review Checklist

  • Spec coverage:
    • 一级导航微信式化Task 2
    • 会话首页极简列表Task 2
    • 聊天页只保留项目目标/版本记录Task 3
    • 设备页 / 我的页简单列表Task 4
    • 返回逻辑与状态保持Task 2 + Task 4
    • 构建 / 真机 / 部署 / 文档Task 5
  • Placeholder scan:
    • No TBD / TODO / “similar to”
    • Every task has exact files, commands, and code snippets
  • Type consistency:
    • WechatSurfaceMapper is the single contract source for root tabs, conversation rows, device rows, and quick actions