Issue #60 / Track #69。
docs/runbooks/navigation-pc-closed-loop-implementation.mdの手順0〜3だけを実装する、一度限りの実装指示である。完了後はdocs/archive/へ移す。
実機、トラクター、Droggerを使わず、PC、Android Emulator、debug専用NMEA走行シミュレータだけで次を完成させる。
このTrackでは、ナビ開始、疑似完走、横ずれ案内、作業セッション、.dwork、実機確認を実装しない。開始前previewの利用者検収で止める。
次の順で全文を読む。
AIエージェント作業規約.mdSTARTUP_CONTEXT.mddocs/README.mddocs/standards/ui-design-guidelines.mddocs/specifications/p6-ui-spec.mddocs/runbooks/navigation-pc-closed-loop-implementation.mddocs/runbooks/nmea-simulator-maintenance.mddocs/runbooks/nmea-simulation.mddocs/standards/architecture-map.mddocs/archive/p6-ui-redesign-*は履歴であり、現行UI規範として使わない。
セッション開始時にButlerのwork_session_start(project_root)、work_observe()、track_summarize(parent_issue_id=69)を実行する。gitコマンドを直接使わない。Pythonが必要なら必ずuv経由にする。
コードより先に、今回確定した仕様をdocs/specifications/p6-ui-spec.mdへ反映する。実装手順書だけを製品仕様の正にしない。
少なくとも次を記載する。
textbook-basic-tillage、version 1。仕様追記だけの境界で文書整合を確認し、Butlerへ判断文脈と作業事実を記録する。
計画生成、列数・重ね幅探索、入口判定をCompose、Android、TypeScriptへ置かない。Android非依存のtrack-coreに純粋domain・純粋plannerとして実装し、:appとpc-toolから同じものを呼ぶ。
既存FieldGeometryOpsはinternalのままとし、JTS型を外部へ漏らさないpublic facade FieldPlanningGeometryを追加する。既存CandidateRouteGenerator、ReferenceRoute.Straight、GeoMathを再利用し、平行線・座標計算を別実装しない。既存Curve対応を壊さない。
計画を少なくとも次へ分ける。
周回1/2/3列目のgeometryはheading変更後も同一でなければならない。全体geometryを一括回転してはならない。一方、追い詰めactionの実行順と挿入位置はheadingに応じて変わり得るため、「追い詰めはすべてAPPROACHに属する固定経路」と扱わない。
追い詰めは四隅ごとの独立作業として、隅ID、geometry、完了予定位置を持たせる。通常方向では外周一周中に4件を完了する。90度回転時は、最初の外周走行で完了する3件と、隣接耕の途中に挿入する1件へ分かれる。後者は隣接耕を中断 → 未完了隅へ移動 → 追い詰め → 中断位置へ復帰 → 隣接耕再開を明示action列で表現する。
各RouteActionに工程、順序、進行方向、geometry、役割、作業種別を持たせる。作業種別は最低限TILL、DEADHEAD、DOUBLE_TILL。空走りを耕耘済みとして扱わない。
ProfileのoverlapWidthCmは基準値であり、実計画値とは分ける。最後の隣接耕を3列目周回上の理想合流点へanchorし、遠い側へ逆算する。開始余白、終端、列数の偶奇、実ピッチ、実重ね幅を同時に満たす解を探索する。
最大調整量は基準重ね幅からの絶対差に対する上限とし、増加・減少の双方を許す。ただし常に0 <= actualOverlapCm < implementWidthCm。複数解の優先順位をdomainで明文化し、同じ入力から同じplanを返すこと。成立しない場合は設定どおりDEADHEADまたはDOUBLE_TILLを明示actionとして入れる。
アルゴリズム上、新たな農作業上の選択が必要になった場合は推測して実装しない。疑問をまとめてIssue #69へ記録し、利用者へ一度に提示する。
優先順位は登録入口、直近fix列の圃場外→内の境界横断、境界上の手動指定。推定入口は確認なしに保存しない。1筆に複数入口と既定入口を許す。直近fix bufferは.dtrkへ依存させずメモリ上に保持し、プロセス再作成後は登録または手動へfallbackする。
plan入力には入口ID、境界座標、進入方位、取得元REGISTERED / RECENT_CROSSING / MANUAL_BOUNDARYを含める。
各境界を独立して記録し、その境界で全テストとrelease buildをgreenにする。後続コードを前倒しで混ぜない。
track-coreへ、名前は既存命名規則に合わせて次相当を追加する。
TillagePlanTillagePhaseRouteActionRouteActionTypeEntranceSnapshot / EntranceSourceOverlapAdjustmentTillagePlanningSettingsTillagePlanningSettingsは最大自動重ね幅調整量と既定fallbackを持ち、実行時の耕耘幅を使って0..implementWidthCmを検証する。初期50cmも耕耘幅を超える場合は実効値を耕耘幅へclampする。
人間が読めるfixtureを作り、最低限、矩形、斜行、狭い、凹形、入口違い、列数偶数・奇数を含める。期待する工程順、作業種別、方向、始終端、周回列番号、基準/実重ね幅、実ピッチ、fallbackをfixtureまたはtest名から追えるようにする。
境界Aの必須test:
DEADHEADを耕耘扱いにしない。FieldPlanningGeometry、固定経路生成、入口正規化、中央隣接耕生成、重ね幅探索、全工程composerを実装する。pc-tool guidance-planを拡張し、筆、入口、Profile、heading、最大調整量、fallbackからplan JSONとGeoJSONを出す。
JSONはこのTrackでは.dwork正式codecではない。PC検証用出力とし、後続保存形式を先取りしない。ただしdomain情報を欠落させない。
境界Bの必須test:
CandidateRouteGeneratorTestと実筆fixtureを壊さない。Layer0SettingsScreenへ次を追加する。
専用store interfaceとSharedPreferences実装を作り、UIからSharedPreferencesを直接操作しない。破損値・範囲外値を正規化して診断ログへ残す。現在のProfileで耕耘幅が変わった場合は上限と表示値を更新する。
有効なProfileがない場合は最大調整量入力を無効化し、既存のProfile不正表示と整合する「Profile設定が必要」の説明を出す。仮の耕耘幅を使って設定・計画を成立させない。
WorkMenuの「圃場全体を計画」から、筆・入口確認、方向編集、開始前previewへ進む。地図上の常設ボタンや上部ステータス列を追加しない。主操作は状態ごとに一つ。回転操作は方向編集中の段階パネルへ置き、1度、長押し連続、90度、初期方向へ戻す操作を提供する。
previewには次をすべて表示する。
TILL / DEADHEAD / DOUBLE_TILL。色だけでなく線種、太さ、opacity、番号、矢印、文言を併用する。既存の現在地、軌跡、耕耘あと、筆輪郭、A-B表示と混同させない。warning表示でWorkDockや主操作の位置を動かさない。回転連打中に保存やevent記録を行わない。
この境界では「ナビ開始」を実装しない。preview生成、回転、入口変更、設定変更後の再計算、取消までとする。
既存GuidancePlanStateを場当たり的なBooleanで増築しない。今回必要なFieldSelecting、入口確認、DirectionEditing、PatternPreview、Ready相当を純粋reducerで表す。ただし後続のNavigatingや保存副作用を先行実装しない。
計画状態の正をComposeのrememberだけに置かない。既存Service/ViewModel境界を確認し、後続作業セッションへ移行可能な所有者を選ぶ。一方、このTrackでは作業セッション永続化を実装しないため、プロセス再作成後に勝手に計画・案内を再開してはならない。
既存GuidanceAvailability.Suspendedは案内可否の直交状態として維持し、同名の作業段階を作らない。
自動test後、既存runbookどおりPCシミュレータとAndroid Emulatorを起動し、実際のDrogger Tablet debug APKを操作する。
最低限、次を目視・操作確認する。
利用者検収前に、画面キャプチャと、入力条件・期待値・実結果をIssue #69へ記録する。Compose Previewだけで完了扱いにしない。
各境界で関連testを実行し、最後に次を通す。
cd apps/android-tablet
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew \
:track-core:test \
:pc-tool:test \
:pc-tool:testSimulatorUiTs \
:app:testDebugUnitTest \
test \
:app:assembleDebug \
:app:assembleRelease \
:pc-tool:installDist \
--max-workers=1
release APKのmanifest/dexへdebug simulatorのservice、TCP command、class名が混入していないことも既存runbookどおり検査する。
既存testが新仕様で落ちる場合、そのtest修正を同じ境界へ含める。赤い状態の中間記録を作らず、失敗を後続境界へ先送りしない。
FieldGeometryOpsをpublic化しない。JTS型を:appへ漏らさない。.dtrkを変更しない。.dworkを先行実装しない。Suspendedという別の作業段階を追加しない。作業開始時にIssue #69配下で、この実装単位のTrackを開始する。境界A〜Cごとに、判断、発見、fallback、実行testを記録する。仕様変更が必要ならコードより先にIssue #69へ理由・代替案・影響を記録し、利用者確認を得る。
完了時には次を報告する。
利用者が開始前previewを確認するまではTrackを完了扱いにせず、現地作業へ進まない。