C. Update - Liplus-Project/liplus-language GitHub Wiki

更新同期手続き仕様書

本文書は Li+ のアダプター / 設定の更新同期手続き(Li+update.md)の仕様を定義する。 Li+config.md の設定値を前提とし、アダプター sentinel tag・Li+config schema・workspace 言語契約のいずれかが目標状態から逸脱した時に AI が実行する Phase を記述する。


概要

更新同期手続きは Li+update.md に定義されている。Li+config.md はユーザー設定のみを保持し、同期ロジックは分離されている。

on-session-start.sh hook が 3 軸(adapter sentinel tag / Li+config schema / 言語契約)を verify し、いずれかが drift していれば LI_PLUS_UPDATE_STATUS=needed を emit する。AI はこの marker を見て本手続きを実行するか判定する。needed の間、on-user-prompt.sh hook は adapter sentinel tag がセッション開始時に記録された対象タグに追いつくまで、同じ marker を毎ターン再掲する(#1987。条件の詳細は 6. Adapter の on-user-prompt.sh)。大半のセッションでは LI_PLUS_UPDATE_STATUS=unnecessary となり、本手続きは走らない(旧称「セッション起動フロー」が現運用とずれていたため、v1.17.10 で「更新同期手続き」へ rename した)。

AI は Li+config.md を読み込んだ後、Li+update.md の Phase 1 から Phase 6 を順に実行する。各 Phase は直前までの Phase を依存前提として宣言する。認証情報をチャットに出力してはいけない。


Phase 1: 環境検出

参照: Li+update.md Phase 1。依存なし。

1.1. ランタイム環境の自動判定

環境変数 判定結果
CODEX_HOME または CODEX_THREAD_ID が存在 runtime=codex
CLAUDECODE が存在 runtime=claude
どちらもなし ユーザーに1回確認し、回答で続行

1.2. Li+config.md のパーミッション保護

Li+config.md にはトークンが含まれるため、ファイルパーミッションを制限する。

  • Linux/Mac: chmod 600 Li+config.md(owner のみ read/write)
  • 既に 600 以下の場合はスキップ
  • Windows: スキップ(ユーザープロファイル配下では NTFS ACL が既に制限済み)

Phase 2: 認証と設定

参照: Li+update.md Phase 2。依存: Phase 1(ランタイム検出済み)。

2.1. gh CLI のインストール(host OS 別、runtime=claude は hook へ移譲済み)

host OS は adapter 種別(runtime=claude / runtime=codex)から推測しない。両 adapter とも Linux / macOS / Windows のいずれのホストでも動作しうる(Claude adapter が Windows ネイティブ Git-Bash/MSYS2 上で動く構成も検証済み、#1518)。ただし自動インストールを行うかどうかは adapter によって異なる(下記 runtime=claude / runtime=codex を参照。runtime=codex はいずれの host OS でも自動インストールしない)。

  • runtime=claude: インストール判断・実行は Li+update.md walkthrough ではなく adapter/claude/hooks/on-session-start.sh hook が毎セッション自律的に担う(bootstrap walkthrough 側は install 手順を持たない)。hook は起動時に無条件で $HOME/.local/bin を PATH に前置し(全ホスト共通の前処理)、その上で command -v gh を単一の gate として評価する。PATH 上のどこかに gh が解決できれば(例: システム標準の /usr/bin/gh でも真になる。~/.local/bin/gh というファイル1つの存在確認ではない)その場でスキップする。gh が見つからない場合のみ uname -s で host OS を判定し、以下のように分岐する:
    • Linux: アーキテクチャ判定つきで ~/.local/bin/gh へ自動インストールする(sudo 不要、PATH 変更不要)。hook の PATH 前置は hook 自身のプロセスに閉じており、後続の Bash ツール呼び出しの親プロセスにはならず、シェルプロファイルへの書き込みも行わない。そのため自動インストール直後でも素の gh が PATH 解決される保証はなく、以降の gh 操作は常にフルパス ~/.local/bin/gh を使用すること(B. Configuration の注意事項にも同旨の記載がある)。この制約は自動インストール対象の Linux にのみ適用される。macOS / Windows は brew / winget が導入した gh が最初から PATH 上にあるため該当しない。
    • macOS / Windows(Git-Bash・MSYS2・Cygwin): 自動インストールしない。gh を前提条件として扱い、brew install gh / winget install --id GitHub.cli の具体的な導入コマンドをユーザーへ案内する。
    • 上記いずれにも一致しないホストカーネル: 自動インストールせず、具体的なコマンドは示さず一般的な導入案内のみを出す。
    • 結果(installed / failed: <詳細> / missing: <ガイダンス>)は GH_INSTALL_STATUS として ━━━ gh install ━━━ マーカーで session 冒頭 context に emit される。マーカーの出力形式・出力タイミングの正本は 6. Adapter の on-session-start.sh 節を参照。
  • runtime=codex: host OS に関わらず gh は前提条件として扱い、bootstrap では自動インストールしない。gh が不在なら検出した host OS に応じた案内(Windows: winget install --id GitHub.cli / macOS: brew install gh)をユーザーへ提示し(代行実行しない)、導入後に続行する。詳細は D. Installation の前提条件を参照

2.2. GH_TOKEN の読み込みと認証

GH_TOKEN を読み込んで gh CLI で認証する。認証情報はチャットに出力しない。認証後は keyring にトークンが保存されるため、以降の gh コマンドに GH_TOKEN の明示的な export は不要。

2.3. workspace 言語契約の解決

LI_PLUS_BASE_LANGUAGE と LI_PLUS_PROJECT_LANGUAGE を解決する。

  • これらは 配布先 workspace 専用 の設定であり、LI_PLUS_REPO 内部のガバナンスとは分離する
  • LI_PLUS_BASE_LANGUAGE は人間との対話の既定言語。issue/discussion/PR コメントのような会話返信もこちらが既定
  • LI_PLUS_PROJECT_LANGUAGE は issue / PR / commit body や保存する要求仕様書など durable artifact の既定言語
  • どちらか未設定の場合、AI がセッション開始時に1回対話で確認し、Li+config.md へ書き戻す
  • 推奨初期値は「基本言語 = 現在の対話言語」「プロジェクト言語 = 基本言語と同じ」
  • bootstrap の ask と Li+config.md への書き戻しは、セッション開始時の config 未解決パスにのみ適用する。config が解決済みなら、セッション途中の再 ask と config 再書き込みは本 Phase の対象外とする
  • runtime precedence(人間の明示指示 > スレッド合意 > config > 再 ask)はアダプターの Workspace_Language_Contract が担い、セッション全体を通して本 Phase を再起動せずに働く

2.4. Webhook 配信モードの解決(任意)

  • LI_PLUS_WEBHOOK_DELIVERY(poll / channel / mcp_hook)はアダプターが runtime で参照する
  • 未設定時の既定は poll。bootstrap 側の追加処理は不要
  • mcp_hook は opt-in 経路(settings.json への手動編集が必要)。詳細は B. Configuration を参照

Phase 3: Li+ ソース解決

参照: Li+update.md Phase 3。依存: Phase 2(gh CLI 認証済み)。

3.1. LI_PLUS_CHANNEL による対象バージョンの決定

  • latest: Latest release タグ(stable release のみ)
  • release: pre-release を含む最新リリースタグ(GitHub Release API)
  • tag: tag 名のバージョン順で最新の git タグ(GitHub Release が未作成のタグも含む)。git ls-remote --tags --sort=-v:refname {repo_url} | head -1 を使用(ローカルリポジトリを要しないため api モードでも同じ literal で解決する)。-v:refname は ref 名だけを読むため、リポジトリ外でも shallow clone 内でも解決できる(object data を要求する -creatordate は落ちる)
  • 包含関係は tag ⊇ release ⊇ latest。tag は GitHub Release 作成前の pre-release タグ検証を意図する
  • バージョン確認は起動のたびに Phase 4 へ進む前に必ず実施する。ローカル clone が古いままでも黙って継続してはいけない

3.2. LI_PLUS_MODE による Li+ ソース取得

api モード:

api モードが clone モードと違うのはソースの取得元だけであり、Phase 4 が生成するものも、インストールされた hook の振る舞いも両モードで同じである。clone は置かない。

  1. {target_tag} の tree を GitHub から tarball として取得し、アーカイブ先頭の単一ディレクトリを落として展開する: gh api repos/{owner}/{repo}/tarball/{target_tag} | tar -xz --strip-components=1 -C {resolved_source_root} {resolved_source_root} = {workspace_root}/.liplus-extract/{target_tag}/(clone モード手順 3 と同じ展開先。無ければ親ディレクトリごと作成する)。この対象タグ用のディレクトリが既に存在する場合は再抽出をスキップする。clone モード手順 3 の任意の後片付けもそのまま適用する
  2. ソースファイルは {resolved_source_root} で参照可能な状態になり、以降は clone モード手順 4 がそのまま適用される(Phase 4 が LI_PLUS_REPO から読むソースパスはすべて {resolved_source_root}/<path> を指す)

clone モード:

  1. 対象リポジトリは LI_PLUS_REPO の対象バージョン
  2. ワークスペース内に LI_PLUS_REPO 由来のディレクトリが存在するか確認
    • 存在しない → 次のコマンドを実行して clone をワークスペースに配置する git clone {LI_PLUS_REPO} {workspace_root}/{repo_dir} 実行する literal はこれのみで、フラグを追加しない。この後に対象タグへの checkout を続けない:下の手順 3 は clone の object database からタグを直接読むため、git clone が残す checked-out branch が何であってもソース解決には無関係であり、そのまま触れない。手順 3 へ進む
    • 存在する → fetch --tags を実行し: a. clone が branch を fetch できるかを確認する。git -C {workspace_root}/{repo_dir} config --get-all remote.origin.fetch が、source 側を refs/heads/ 以下に持つ refspec を最低 1 本保持していること。1 本も無い clone でも tag は解決するため、直前の fetch --tags は成功したまま branch はどれも動かず、後続の素の git fetch origin もエラーではなく no-op として成功する。無い場合は人間にその事実を名指し、代償も名指す(ローカル branch が永久に進まないため、ローカル branch から生やした worktree やビルドは古い木から取られる)。検出のみであり、refspec の追加も re-clone も行わず、中断もしない(手順 3 へ続行する)。修理は人間の側にある(共有されたローカル git state を書き換えるため、エージェントが独断で踏まない)。同じ条件は on-session-start hook が毎セッション surface しており、その状態に留まる clone はそこで報告され続ける。載せ先と、それが LI_PLUS_UPDATE_STATUS でない理由は 2. Evolution の Cold-start Synthesis 節(Clone Branch Fetch Surface とフックの出力契約) この手順のあと HEAD が何であるか——branch に attach しているか、別タグで detached か、他セッションの操作の途中か——は、確認も分岐もしない。手順 3 は {target_tag} を object database から直接読み、working tree にも HEAD にも依存せず、干渉もしない。旧来の attached-HEAD STOP と、現在タグ/対象タグの比較(checkout {target_tag} の実行可否をここで判定していた部分)は、判定すべき HEAD 移動そのものが手順 3 では発生しなくなったため、判定先を失って両方とも削除した。
  3. {target_tag} の tree を、clone の HEAD にも working tree にも触れずに読み取り専用で取得する: git -C {workspace_root}/{repo_dir} archive {target_tag} | tar -x -C {resolved_source_root} {resolved_source_root} = {workspace_root}/.liplus-extract/{target_tag}/(無ければ親ディレクトリごと作成する)。この対象タグ用の {resolved_source_root} が既に存在する場合は再抽出をスキップする この抽出は、本リポジトリの .gitattributes(.md / .sh の LF 正規化、先頭 BOM を保つ -text の .ps1)のもとで通常の checkout と byte 一致する。.gitattributes がこれと大きく異なるリポジトリでは、利用前に再検証すること 任意の後片付け:.liplus-extract/ 配下の、現在の対象タグ以外のタグの抽出ディレクトリは削除してよい。対象タグが進んだ時点で Phase 4 はそれを読まなくなる
  4. ソースファイルは {resolved_source_root} で参照可能な状態になる。Phase 4(clone モード)が LI_PLUS_REPO から読むソースパスはすべて——LI_PLUS_REPO/ 接頭辞付き(LI_PLUS_REPO/rules/)で書かれていても、接頭辞なしのリポジトリ相対パス(adapter/claude/CLAUDE.md、adapter/claude/hooks/*.sh、adapter/codex/AGENTS.md、adapter/codex/hooks/*)で書かれていても——{workspace_root}/{repo_dir} の working tree ではなく {resolved_source_root}/<path> を指す。読み込みは Phase 4 が担う

Phase 4: ホスト統合

参照: Li+update.md Phase 4。依存: Phase 3(ソース解決済み、対象タグ確定済み)。

ランタイム固有の統合処理。検出した runtime で分岐する。adapter / rules / skills / hooks を生成する。rules/skills の生成はレイヤーの読み込みを兼ねる(生成された .claude/rules/ .claude/skills/ をホストが毎ターン読むため明示 read は不要)。

生成物には {LI_PLUS_TAG} プレースホルダがあり、bootstrap 時に Phase 3 で解決済みターゲットタグへ置換する。

共通判定ロジック

自動スキップ・自動置換は Li+ BEGIN sentinel 検出時にのみ適用する。sentinel 不在(legacy file)はユーザー判断を必須とする。legacy file を暗黙に上書きすると、ユーザー自身が書いた内容を同意なく破壊することになるため禁止する。

Phase 4 claude: Claude Code 統合

4c.1. アダプターの bootstrap

  • target = {workspace_root}/.claude/CLAUDE.md, source = adapter/claude/CLAUDE.md
  • ファイルが存在しない → ソースの内容で新規作成
  • ファイルが存在し Li+ BEGIN sentinel を含む:
    • sentinel 内のタグ(例 Li+ BEGIN (build-2026-03-30.14) → build-2026-03-30.14)を抽出
    • 現在のターゲットタグと一致 → スキップ(最新)
    • 不一致またはタグなし → Li+ BEGIN 〜 Li+ END 間(両端含む)をソース内容で差し替え、下記 legacy webhook trailer migration を除く sentinel 外は保護。置換 byte span は Li+ END の最終 byte で終え、adapter source の EOF newline を保持する target suffix へ追加しない
    • legacy webhook trailer migration: 現行 adapter source が ## Optional Webhook Notification Flow block を sentinel 内で所有する。migration は旧 sentinel section に webhook heading が無い pre-migration 所有形状だけで実行する。差し替え前に source の当該 heading から Li+ END 直前までを legacy block として取得し、旧 target の Li+ END 直後の suffix から、先頭の2個の separator newline、legacy block、block末尾 newline までを1件とする連続した byte-exact legacy trailer だけを除去する。最初の不一致 byte で停止し、line ending を正規化して一致させず、以後の suffix はそのまま保持する。これにより pre-migration 版が残した重複 trailer を除去しつつ、canonical sentinel の外へ後から追加された同一 block を削除しない。後続 tag を再適用した時は旧 section が既に heading を所有するため migration を skip し、migrated layout の webhook heading は置換 section 内の1件だけになる
  • ファイルが存在するが sentinel なし → ユーザーに確認(Li+ セクションを追記 or スキップ)

4c.2. .claude/rules/ ファイル生成(再帰ディレクトリミラー)

  • {workspace_root}/.claude/rules/ が存在しなければ作成
  • LI_PLUS_REPO の rules/**/*.md を再帰走査し(model/ evolution/ task/ operations/ subdir を含む)、rules/model/character_Instance.md を除いた各ファイルについて:
    • LI_PLUS_REPO/rules/ からの相対パスを保持してターゲットへ配置する(例: rules/model/absolute.md → .claude/rules/model/absolute.md)
    • ターゲットが存在しない、またはソースタグが現在のターゲットタグと異なる場合、ソース内容をコピー。ソースは既に globs: + alwaysApply: true + layer: frontmatter を含む
    • 必要なら subdir を作成
    • ソースタグが一致する場合はスキップ
  • character_Instance.md の生成(output-styles slot):
    • source body = LI_PLUS_REPO/rules/model/character_Instance.md(rules-format frontmatter を除去した body 部、codex adapter と共有)
    • target = {workspace_root}/.claude/output-styles/character_Instance.md
    • 付与する output-styles frontmatter: name: character_Instance + description: Lin/Lay character pair binding for human-facing dialogue + keep-coding-instructions: true(このフラグを付与しないと、custom output-style 有効化時に Claude Code 既定のコーディング作法 / TodoWrite / ツール使用ガイダンスが system prompt から除外される。出典: https://code.claude.com/docs/en/output-styles.md)
    • 旧 rules slot からの一回限り migration:
      • 旧 file {workspace_root}/.claude/rules/model/character_Instance.md が存在し、かつ target が存在しない場合: 旧 file の body(rules frontmatter 除去後)を読み、output-styles frontmatter + body を target に書き、書き込み成功後に旧 file を削除する(ユーザーカスタマイズを新位置へ保全)
      • 旧 file と target が両方存在する場合: いずれにも触れない(ユーザーが既に migrate 済み or 手動介入したと見なし、現状を保護)
    • 新規 install(旧 file なし):
      • target が存在しない場合のみ source body + output-styles frontmatter を target に書く
      • target が存在する場合はスキップ(Create-only)
    • 必要なら {workspace_root}/.claude/output-styles/ subdir を作成
    • タグベースの上書きは行わない。ユーザーカスタマイズはアップデートをまたいで保護される
  • 古い rules の削除: {workspace_root}/.claude/rules/ 配下で LI_PLUS_REPO/rules/ の対応パスに存在しないファイル(ただし {workspace_root}/.claude/rules/ 起点の相対パスが model/character_Instance.md でないもの)は削除。空になった subdir も削除(model/character_Instance.md 例外は migration が走らなかった「両方存在」ケースに対する safety net として残置)

4c.3. .claude/skills/ ファイル生成(flat ディレクトリミラー)

  • {workspace_root}/.claude/skills/ が存在しなければ作成
  • LI_PLUS_REPO の skills/<name>/SKILL.md を flat に走査する(subdir は持たない):
    • target = .claude/skills/<name>/SKILL.md
    • 必要なら subdir を作成
    • ソースをそのままコピー(ソースは既に Claude Code skill frontmatter を含む)
    • ソースタグが一致する場合はスキップ
  • 古い skills の削除: .claude/skills/ 配下で LI_PLUS_REPO/skills/ に存在しない <name>/ ディレクトリは再帰削除

注意: Claude Code の skill 探索は .claude/skills/ 配下の subdir を辿らない。skill 名は flat 階層で一意である必要があり、レイヤー属性は skill 名の接頭辞規約で表現する(例: evolution-judgment-learning)。

4c.4. hooks の bootstrap

  • ソースファイル:
    • adapter/claude/hooks-settings.md — settings.json の JSON ブロックをリテラルで保持
    • adapter/claude/hooks/*.sh — hook スクリプト本体(そのままコピーし、{LI_PLUS_TAG} プレースホルダを解決済みターゲットタグへ置換)
  • {workspace_root}/.claude/settings.json が存在しない:
    • adapter/claude/hooks-settings.md の JSON コードブロックから settings.json を生成
    • {workspace_root}/.claude/hooks/ を作成し、adapter/claude/hooks/*.sh をすべてコピー
    • SessionStart は startup / resume / clear / compact / fork の 5 matcher(一次ソースの matcher 表の全件)を使用し、Cold-start Synthesis 素材がどのセッション入口でも出力されるようにする。未登録の matcher は他の entry にフォールバックせず単に hook が発火しないため、その入口では LI_PLUS_UPDATE_STATUS マーカーも language contract banner も出ない
  • {workspace_root}/.claude/settings.json が存在する:
    • settings.json は変更しない。workspace が所有するファイルであり、Li+ はユーザー追加キー(permissions / env / theme / 他コンポーネント hook)を暗黙に書き換えない
    • 既存の {workspace_root}/.claude/hooks/*.sh 内のソースタグを確認(例: # Source: adapter/claude/hooks/on-session-start.sh (build-2026-03-30.14))
    • 現在のターゲットタグと一致 → スキップ(最新)
    • 不一致またはタグなし → adapter/claude/hooks/*.sh を再コピーし、{LI_PLUS_TAG} を現在のターゲットタグへ置換(settings.json は再生成しない)
  • on-session-start.sh が Cold-start Synthesis 素材の emitter。stdout はセッション開始コンテキストへ注入される(Claude Code SessionStart 契約)。素材は rules/evolution/cold-start-synthesis.md の anchor(H1 preamble のみ)、直近の docs/Decision-Structure.md 先頭、rules/ のパスツリー、最新リリースタグ、open in-progress issue、open な blocker を持つ open issue、self-evaluation 先頭、promotion candidates、self-evolution observation surface(due / overdue)。素材の正本一覧と section key は 6. Adapter — on-session-start.sh を参照する。rules / skills / docs から読む素材(cold-start anchor、Decision-Structure 先頭、rules ツリー、promotion candidates の rules / skills 本文キーワード走査)は、clone の working tree ではなく、adapter 自身のインストール済み sentinel タグに固定した LI_PLUS_REPO clone の git archive 抽出から読む(sentinel タグが解決できない場合や抽出に失敗した場合は working tree にフォールバック)。api モードでは同じ読み取りを {workspace_root}/.liplus-extract/{sentinel タグ}/ から行い、無ければ hook が GitHub の tarball から埋める(Phase 3.2 api モード手順 1 と同形)。api モードでは clone が無いことを bootstrap 前とはみなさない。synthesis は hook ではなく Character_Instance を介して AI が行う
  • .sh ファイルには実行権限を付与

4c.5. cold-start state ディレクトリの準備(diff-only 出力の永続化)

  • on-session-start.sh は session 冒頭 context 消費を抑えるため、各素材 section の sha256 fingerprint を {workspace_root}/.claude/state/last-cold-start-emit.json に永続化し、次回の startup matcher 発火時に変化した section だけを emit する(毎セッション全文出力は diff-only 設計の意義を打ち消す)。

  • {workspace_root}/.claude/state/ が存在しなければ作成する

  • {workspace_root}/.claude/state/.gitignore が存在しなければ以下のリテラルで作成する(ユーザー変更済みの場合は上書きしない):

    # Li+ hook runtime state — local-only, not version-controlled.
    *
    !.gitignore
    

    ローカルスコープの gitignore により、state がバージョン管理されているホストワークスペースに混入しないようにする。トップレベルの .gitignore には触れない。state 本体(last-cold-start-emit.json)は hook が初回実行時に生成する。

  • このステップは冪等:既存ディレクトリと既存 .gitignore はそのまま残す

on-session-start.sh の matcher 別挙動(startup の diff-only、resume/clear/compact/fork の rule anchor 再出力、fail-safe full emit)の詳細は 6. Adapter — on-session-start.sh を参照する。

4c.6. .claude/agents/ ファイル生成(sentinel 区画ミラー)

  • LI_PLUS_REPO/adapter/claude/agents/ が存在しない場合は本サブフェーズ全体をスキップ(adapter に subagent 定義がない/codex 等の非 claude adapter は影響を受けない)
  • {workspace_root}/.claude/agents/ が存在しなければ作成する
  • 生成内容中の {LI_PLUS_TAG} は Phase 3 で解決したターゲットタグへ置換する
  • 所有の境界: agent ファイルは 2 種類の内容を同時に運ぶ。Li+ が判定基準として所有する本文(評価者の判定基準とその任務の射程)と、その周囲にあるユーザーのランタイムインスタンス(frontmatter の name / description / tools / model、およびユーザーが足した記述)である。ファイル単位の所有はどちらか一方しか選べず、どちらを選んでも他方を取り違える — Create-only はマージ済みの基準変更を既存ワークスペースから締め出し、ファイル全体の上書きはインスタンスを破壊する。Li+ BEGIN / Li+ END 区画は境界をファイルの内側へ移し、両方を成立させる。区画が覆うのは prompt 本文のみで、frontmatter は区画外に置く(frontmatter はワークスペースごとのランタイムカスタマイズが載る面であり、加えて Markdown の frontmatter ブロックの前に sentinel 行を置けないため)。Claude 側で受容した代償: frontmatter 以降がそのまま subagent の system prompt になり、Markdown には host が除去するコメント形式が無いため、sentinel 2 行は prompt の内側に入る。prompt に入る inert な 2 行は Markdown 側で伝播を得るための対価であり、これを避ける唯一の代替は当該ポートに区画を持たないことである
  • どのソースが区画を持つかは列挙ではなく基準で決める。 順に 2 つ問う:
    1. そのファイルは、ユーザーの instance ではなく Li+ の判定基準として所有する本文を運んでいるか。 No → Create-only。基準がこの問いのどちら側にも置けないファイルも Create-only を既定とする
    2. その本文を、ユーザー所有の内容を巻き込まずに 1 つの連続区画で覆えるか。 Yes → 区画を持つ。No(2 種類が交互に並ぶ)→ Create-only とし、そのファイルの伝播欠落は開いたまま残す。交互配置をまたぐ幅の区画はユーザーの内容を飲み込み、それは区画が防ぐために存在する失敗そのものである。問い 1 だけでは混在ファイルは決まらず、決まると読むと逆の答えが出る
  • 問い 2 に現時点の事例は無い。 adapter/*/agents/dialogue-evaluator.* が唯一の事例だったが、区画を広げるのではなく書き手側で解いた — 判定基準の間に挟まっていた Character_Instance literal は spawn プロンプト経由で届くようになり、本文に残るのは Li+ 所有のみとなって 1 つの区画で覆える。ただし問い 2 自体は生きている。これはファイルを決める問いであって、どのファイルが過去に落ちたかの記録ではない。No は依然として Create-only を意味し、そのファイルの伝播欠落は開いたまま残る
  • No に当たった書き手が取れる手は、区画を広げることではなく、ユーザー所有の内容を既存の経路で本文の外へ出すことである。そうした経路が無ければ No のまま据え置く
  • 1 つのソースが持つ区画は最大 1 つで、sentinel 文字列はそのファイルの他の場所(コメントを含む)に現れてはならない。区画は最初の出現位置で特定するため、2 つ目の言及が境界として読まれる
  • LI_PLUS_REPO/adapter/claude/agents/ 直下の *.md 各ファイルについて(FLAT、サブディレクトリなし):
    • target = {workspace_root}/.claude/agents/<filename>.md
    • sentinel を持たないソース(Create-only): target が存在しなければソースをそのままコピー、存在すればスキップ(ユーザーカスタマイズを保持)
    • sentinel を持つソース — 区画判定(4c.1 と同型、本ファイルへスコープ):
      • a. target が存在しない → 解決済みソースの内容で生成する
      • b. target が存在し Li+ BEGIN を含む → sentinel からタグを抽出。現ターゲットタグと一致すればスキップ。異なる/欠落していれば Li+ BEGIN〜Li+ END(両端含む)の区画をソース側の区画で置換し、区画外(上の frontmatter とユーザーが下に足した記述)はそのまま保持する。4c.1 の legacy webhook trailer migration は本分岐では適用しない(あの migration は CLAUDE.md 面の byte-frozen な ## Optional Webhook Notification Flow ブロック専用であり、agent ファイルに該当する pre-migration trailer は存在せず、ここで走らせるとユーザー記述を削除する)
      • c. target が存在するが Li+ BEGIN を含まない → ユーザーへ確認:現ソースからこのファイルを再生成するか、スキップするか。区画導入以前のインストールはすべてこの状態から入る。sentinel の無いファイルのどのバイトがユーザー由来かを Li+ は判別できないため、再生成はファイル全体を置き換える(当該ファイルへのローカル編集は失われる — 事前にコピーを取るよう案内する)。スキップは Li+ 所有の基準をインストール時点のまま据え置く。無音の上書きは 4c.1 と同じ理由で禁止(ユーザー記述を同意なく破壊するため)
        • 再確認の周期と、それが何に寄生しているか: ユーザーの回答を記録する state は無く、branch (c) は本サブフェーズが走るたびに target ファイルのバイトから判定し直す。したがってスキップしたファイルは次に bootstrap が走ったとき再び確認される。bootstrap が走るのはセッション開始時の marker が needed を返すときであり、そのタグ軸が読むのは .claude/CLAUDE.md の sentinel であって agent ファイルではない(agent ファイルの sentinel 状態を読む面は存在しない)。4c.1 はここへ到達するのと同じ walkthrough 内でその sentinel を更新するため、次の再確認は次のタグ更新=次のリリースで届く。リリースとリリースの間、スキップを選んだワークスペースは追加のプロンプトなしにインストール時点の基準のまま座る。これは branch (c) が隠す欠陥ではなく残余である — 区画導入前の挙動にはプロンプト自体が一度も無かった
  • stale 削除はしない(ユーザーが adapter ソースに無い custom subagent を保持している可能性があり、stale 扱いすると user work を破壊する)。Li+ ソース側が削除された target も同じで、bootstrap は残置する。既存ワークスペースはソース削除後もそのファイルを保持し、削除はユーザーの手に残る。残置されたファイル自体は不活性である — agent ファイルは名指しの spawn でしか到達されず、rules / skills / adapter のどこもそれを名指さなくなった時点でそこへ至る経路が無い

注意: bootstrap は次回セッションから有効。現セッションは Li+config.md の実行で継続する。

Phase 4 codex: Codex 統合

Codex ホストでは Phase 4 claude branch と同型に adapter / rules / skills / hooks を生成する(#1502 実機検証済みの Codex 配置)。agents だけは生成しない。

  • skill は .agents/skills/<name>/SKILL.md に配置(Codex ネイティブの description 自動発火、trust 不要)
  • 常時 rules には Codex 側のフォルダ相当が無いため、SessionStart hook が導入済み .codex/rules/**/*.md を読み additionalContext で注入する(.codex/hooks/on-session-start)。旧 branch の「bootstrap で rules を直接読む」ステップは廃止
  • hook は .codex/hooks/(*.ps1 が Windows ネイティブ主経路 + *.sh POSIX フォールバック)に配置し、.codex/hooks.json で登録
  • subagent の定義ファイルは置かない。Codex の子は組み込みエージェントとして spawn し、役割の説明は spawn プロンプトが、model と reasoning_effort は spawn 呼び出しが運ぶ(#2176)。本 branch は .codex/agents/ に何も書かず、何も消さない。以前の build が置いた implementer.toml / brake-evaluator.toml / dialogue-evaluator.toml は既存ワークスペースに残るが、どの spawn も名指さないため不活性である。片付けはそのワークスペースを運用する人の手に残る

Codex hook trust 前提条件(ユーザーへ明示): Codex の hook は実行前に一度だけ GUI trust が必要(Codex App → 設定 → フック → 当該プロジェクト → 信頼する)。build が hook 本体を変えるたびに再 trust が必要(trust は内容ハッシュ単位)。trust 前は SessionStart の rules 注入と毎ターンの gate 再注入が無音で何もしない。bootstrap は hook ファイルを書くが trust は付与できないため、Phase 6 完了報告で GUI trust を案内する。詳細は D. Installation を参照。

4x.0. アダプター置換前のキャラ保持

  • 最初に .codex/output-styles と project [liplus] output_style を検証する。選択未設定+フォルダありは character_instance、標準欠落は明示失敗。両方なしだけ legacy。検証済み file/false は gate を満たし、以下の native 既定追加を実行しない。共通指示を保持し有効 native persona の併置を移行前に解決する。

  • 以下の native 手順は legacy mode に限る。file mode の再更新で利用者 style を生成し直さない。

  • 実効 developer_instructions と明示的 opt-out を先に確認し、CLI・trusted project・選択 profile・user・managed/system の設定を解決する。profile の選択・形式・trust は変更しない(公式資料と版差は adapter/codex/character-config.md)

  • 旧 AGENTS.md の [Character_Instance] を区画置換・tag 一致スキップより先に読む。一意な sentinel と Responsibilities 境界の内側ならカスタム literal 全文を保存。境界が曖昧・legacy 無 sentinel・TOML 無効・実効設定未解決なら 4x.1 を止め、既存の legacy 判断手順へ戻る

  • Python 3.11+ scripts/migrate_codex_character.py --agents <workspace>/AGENTS.md --config <workspace>/.codex/config.toml --native-state absent|present|disabled の既定は read-only plan。既存キャラと opt-out を優先し、設定を書き換えない。キャラ不在なら初回・再更新とも Lin/Lay の opt-in を提案。既存キャラと明示 opt-out は保持する

  • 初回には Li+ 区画のない通常の既存 AGENTS.md も含む。旧 literal の不在や空の指示値は単なる不在であり、明示 opt-out と判断しない。sentinel 境界が曖昧なら 4x.1 を止める。既存 native 設定/opt-out を保持する分岐より先に境界を検査する

  • 既存 config の共通指示への追記は差分提示・承認後に --apply --approve-existing で保存。新規への opt-in は --apply。旧 AGENTS.md と変更する既存 config を一意名でバイト忠実にバックアップ。保存・read-back に失敗したら config を復旧し、旧区画を保持する。保存した TOML・共通指示・literal(または実効ネイティブ設定 / opt-out)を検証してから 4x.1 へ進む。helper 不在時も同じ保存判断を手動で行う

  • target に root の指示キーが無い場合は、実効設定から継承された共通指示も保持する。private UTF-8 file の --inherited-instructions <file> で helper へ渡し、使用後に削除する。指示本文を command line や report に出さない

4x.0s. project output-style の一回移行

  • 正本は Li+update.md 4x.0s、API は adapter/codex/character-config.md。利用者所有の .codex/output-styles/<name>.md と選択名だけを持つ project config を導入する。scripts/migrate_codex_output_style.py は private file に保存された承認済み一意 persona 境界を保存し、common/無関係な設定・encoding を保持する。既定は read-only、--apply --approve-existing で明示適用、byte backup・同時変更 guard・read-back・失敗復旧・再適用 no-op を行う。
  • 既存 style を上書きしない。明示 --use-existing-style は既存本文を検証して保持し、旧 inline だけを backup 後に除去する。空 common は空キーを残す。個別承認した local config のみ --remove で指定し、account 保存は --preserve-selection で project 選択を保持する。global/profile を自動走査編集しない。
  • Claude 共有リンクは環境移行時に既存内容保護の上で作る。GUI trust・CLI startup/resume・Pullcept 実経路は source CI と別の完了証拠である。

4x.1. アダプターの bootstrap

  • target = {workspace_root}/AGENTS.md, source = adapter/codex/AGENTS.md
  • sentinel 判定ロジックは 4c.1 と同一(存在しなければ新規、sentinel ありタグ一致でスキップ、不一致で section 差し替え、sentinel なしでユーザー確認)。差し替え時の legacy webhook trailer migration も4c.1と同一で、旧 section が heading を持たない時だけ、連続する byte-exact legacy trailer を Li+ END 直後から除去し、canonical sentinel 外のユーザー作成 suffix を保持する
  • 生成 AGENTS.md にキャラ定義・代替項目・参照リンクを残さない。ネイティブ設定は利用者所有のまま更新で再生成しない
  • 32 KiB 上限: ルートの AGENTS.md は最小コア(identity / 起動契約)のみを保持。rules 全体は 4x.3 の SessionStart hook 注入で届くため inline しない(Codex の project_doc_max_bytes 既定 32 KiB を超えないため)

4x.1r. .codex/rules/ の導入

  • 正本は Li+update.md 4x.1r。タグ解決した source の helper を実行し、style/disabled または legacy native 保存確認後に character_Instance.md を除く rules をコピーする。所有 manifest に従って更新・stale 削除し、無関係な追加 md と native execution-policy を保持する。既定は read-only plan、書込前 backup と読戻し・失敗時復旧を行う。

4x.2. .agents/skills/ ファイル生成(flat ディレクトリミラー)

  • 4c.3 と同型だが、Codex ネイティブの skill 配置は .agents/skills/(.claude/skills/ ではない)
  • {workspace_root}/.agents/skills/ が存在しなければ作成
  • LI_PLUS_REPO の skills/<name>/SKILL.md を flat に走査し、.agents/skills/<name>/SKILL.md へそのままコピー(ソースタグ一致でスキップ)
  • 古い skills の削除: .agents/skills/ 配下で LI_PLUS_REPO/skills/ に存在しない <name>/ ディレクトリは再帰削除

4x.3. hooks の bootstrap

  • ソースファイル:
    • adapter/codex/hooks-config.md — .codex/hooks.json の JSON ブロックをリテラルで保持(config.toml [hooks] 代替スニペットも併記)
    • adapter/codex/hooks/*.ps1(Windows ネイティブ主経路)+ adapter/codex/hooks/*.sh(POSIX フォールバック)
    • Python >=3.11 の scripts/codex_output_style.py を .codex/hooks/codex-output-style.py として byte-faithful 導入・内容更新する。独立 SessionStart handler の additionalContextLimit: 0 と helper の 128 KiB 出力上限を使う。既存 rules context limit と他 handler を保持し、hook body 更新後は GUI trust を再確認する。resolve 成功だけで trust 済みとしない。
  • .ps1 のバイト忠実コピー(重要): .ps1 は BOM 付き UTF-8(先頭 3 バイト EF BB BF)。これを呼び出す Windows PowerShell 5.1 は BOM 無しの非 ASCII .ps1 を誤読する。.ps1 は生バイトでコピーし、BOM を剥がす / 付け直す / 改行を書き換えるテキスト変換を通さない。インストール後、各 .ps1 が EF BB BF で始まることを検証する(.sh は BOM 無し LF 終端 UTF-8、こちらもそのままコピー)
  • hook 本体の {LI_PLUS_TAG} 置換: # Source: ... ({LI_PLUS_TAG}) 行の token を解決済みターゲットタグへ置換。BOM とその他バイトを保つためバイトレベルの token 置換で行う(token は ASCII、置換は先頭 BOM に触れない)
  • .codex/hooks.json の {WORKSPACE_ROOT} 置換: Codex の hook は絶対パスが必要($CLAUDE_PROJECT_DIR 相当が無い)。.codex/hooks.json 内の {WORKSPACE_ROOT} をすべて絶対 workspace パスへ置換。スペースを含むパスは quote(テンプレートは -File 引数を既に quote 済み)
  • {workspace_root}/.codex/hooks.json は Li+ 所有(compare-and-overwrite):
    • 存在しない → adapter/codex/hooks-config.md の JSON ブロックから生成({WORKSPACE_ROOT} 置換済み)。{workspace_root}/.codex/hooks/ を作成し adapter/codex/hooks/*.ps1・*.sh をバイト忠実にコピー
    • 存在し内容が byte 一致 → スキップ
    • 存在し内容が異なる → テンプレートで上書き(ユーザー固有の Codex 設定は Li+ 非所有の {workspace_root}/.codex/config.toml に置く。hook を TOML 配置したい場合は config.toml [hooks] スニペットが代替。hooks.json と TOML を両用しない=二重登録になる)
    • SessionStart は単一の regex matcher startup|resume|clear|compact(Codex の matcher は regex、1 エントリで 4 ソースを網羅)。rules 注入 + cold-start 素材はどのセッション入口でも発火
  • {workspace_root}/.codex/hooks/*.{ps1,sh} の tag 追跡再生成:
    • 既存ファイルの # Source: ... (build-...) 行のタグを確認。一致でスキップ、不一致 / タグなしで再コピー(バイト忠実 .ps1 + {LI_PLUS_TAG} 置換)
    • 再生成は hook 内容ハッシュを変えるため Codex の GUI trust を無効化する。完了報告で再 trust を案内する
  • on-session-start が Codex の rules 注入 + Cold-start Synthesis 素材 emitter。rules/**/*.md、および docs/Decision-Structure.md と skills/*/SKILL.md を、clone の working tree ではなく、adapter 自身のインストール済み sentinel タグに固定した LI_PLUS_REPO clone の git archive 抽出から読む(sentinel タグが解決できない場合や抽出に失敗した場合は working tree にフォールバック)。api モードでは Claude 版と同じく {workspace_root}/.liplus-extract/{sentinel タグ}/ から読み、無ければ GitHub の tarball から埋める。rules の literal を additionalContext で注入(Claude の .claude/rules/ 常時フォルダの Codex 代替)+ update-status marker(LI_PLUS_UPDATE_STATUS、startup matcher 限定)+ language contract marker(LI_PLUS_BASE_LANGUAGE / LI_PLUS_PROJECT_LANGUAGE、全 matcher)+ diff-only cold-start 素材。synthesis は hook ではなく Character_Instance を介して AI が行う
  • .sh ファイルに実行権限を付与(.ps1 は powershell -File 経由で呼ばれるため実行ビット不要)

4x.4. cold-start state ディレクトリの準備(diff-only 出力の永続化)

  • 4c.5 と同型。Codex の state パスは .codex/state/

  • on-session-start は各 section の fingerprint を {workspace_root}/.codex/state/last-cold-start-emit.json に永続化する

  • {workspace_root}/.codex/state/ が存在しなければ作成

  • {workspace_root}/.codex/state/.gitignore が存在しなければ以下のリテラルで作成(ユーザー変更済みは上書きしない):

    # Li+ hook runtime state — local-only, not version-controlled.
    *
    !.gitignore
    
  • このステップは冪等

注意: bootstrap は次回セッションから有効、かつ一度きりの Codex GUI hook trust が前提(4x 冒頭)。現セッションは Li+config.md の実行で継続。trust 付与までは rules 注入と毎ターンの gate は走らない


Phase 5: ワークスペース準備

参照: Li+update.md Phase 5。依存: Phase 2(gh CLI 認証済み)。

5.1. USER_REPOn 作業クローンの準備

Li+config.md 内の USER_REPO1、USER_REPO2、… を順に enumerate して以下を実行する:

  • 値がデフォルト値(テンプレート初期値の URL プレースホルダ等)の場合はスキップ
  • 値が LI_PLUS_REPO と一致する場合: ローカル clone で git checkout main を実行
  • それ以外: clone 要否はディレクトリ名ではなく remote で判定する(下記)

clone 要否の判定(remote 照合)

ワークスペース直下1階層の各ディレクトリ(.git を持つもののみ、再帰しない)の git remote get-url origin を読み、entry の URL と Phase 2.5 の正規化形で突き合わせる。.git を持たない、または origin が読めないディレクトリは origin 無しとして扱い、一致しない。Phase 5 は独自の正規化規則を持たない。

走査結果 導出名のディレクトリ 動作
一致1件 無い、または一致したものと同一 一致したパスを作業ディレクトリとして採用し clone をスキップ
一致無し 無い git clone <url>
一致1件(導出名と別のディレクトリ) 在る STOP。両パスと各 origin、entry URL を報告する。どちらを作業ツリーとするかは Li+ が決めてよい判断ではない
一致無し 在る(origin が entry URL と不一致、または読めない) STOP。パスと origin、entry URL を報告する
一致2件以上 — STOP。一致した全パスと各 origin、entry URL を報告する

STOP した entry は未準備のまま残し、clone は行わない。残りの USER_REPO<N> は続行し、STOP した entry は Phase 6 の完了報告に載せる。

リポジトリ改名(例: liplus-chat → pullcept)の後、改名前に clone したディレクトリは名前が追従しない。ディレクトリ名だけで判定すると同一リポジトリの2つ目のクローンが生まれるため、判定軸を remote に置いている。

URL から owner / repository name は parse して抽出する(gh CLI integration 用)。HTTPS / git+ssh / local path / file:// はいずれも受容する(詳細は B. Configuration の USER_REPOn 節を参照)。


Phase 6: 完了報告

参照: Li+update.md Phase 6。依存: すべての先行 Phase。

6.1. 更新同期完了を報告する。

6.2. runtime=codex のみ — 一度きりの GUI hook trust を案内する

  • Codex App を開き hook を trust するよう案内する(設定 → フック → 当該プロジェクト → 信頼する)。trust 付与までは SessionStart の rules 注入と毎ターンの Trigger Check Gate 再注入が走らない(Phase 4 codex 冒頭、step-by-step は D. Installation)。
  • 本 bootstrap が hook 本体を再生成した場合(tag bump)、trust の再付与が必要なことを伝える(trust は内容ハッシュ単位)。

関連ページ


進化

再構築・削除・最適化はすべて許容する。構造の一貫性のみ維持する。

⚠️ **GitHub.com Fallback** ⚠️