···11+<!-- mainline:agents:start version=12 checksum=sha256:bb93a86dcc1512ce1f81dfd84c14e6714261d252997e36eda3e1041a515273ba -->
22+## Mainline
33+44+<!-- mainline-agents-md-version: 8 -->
55+66+This project uses **Mainline** for AI-driven intent tracking and
77+conflict detection. The full agent workflow lives in `AGENTS.md` at
88+the repo root — read that file for the complete contract.
99+1010+Quick reference:
1111+1212+```
1313+mainline status # see your state
1414+1515+# Read team intents for context (do this aggressively):
1616+mainline log --json --limit 30 # recent intents
1717+mainline show <intent_id> --json # full why/decisions/risks
1818+mainline list-proposals --json # what's in flight
1919+2020+# Write your own intent:
2121+mainline start "<goal>" # claim work
2222+mainline append "<what changed>" # after each turn
2323+git add ... && git commit -m ... # commit code
2424+mainline seal --prepare > .ml-cache/seal.json # patch the starter
2525+mainline seal --submit < .ml-cache/seal.json # auto syncs + checks
2626+```
2727+2828+Sync, pin, merge are automatic — do not invoke them.
2929+3030+### If `mainline hooks` is installed for your agent
3131+3232+When hooks are active, the only thing that changes is sessionStart:
3333+the hook runs `mainline sync` + `mainline status` for you and injects
3434+the snapshot into your system context. Every other workflow step
3535+(start / append / commit / seal --prepare / seal --submit / check)
3636+remains agent-driven exactly as `AGENTS.md` describes. Hooks are a
3737+context provider, not a workflow driver.
3838+<!-- mainline:agents:end -->
+14
.github/PULL_REQUEST_TEMPLATE.md
···11+## Summary
22+33+<!-- Describe what this PR does -->
44+55+## Mainline Intent
66+77+<!--
88+This section is auto-filled by mainline pr-description.
99+It is for human reviewers; Mainline does not parse it.
1010+-->
1111+1212+## Tested
1313+1414+<!-- How was this tested? -->
+38
.github/copilot-instructions.md
···11+<!-- mainline:agents:start version=12 checksum=sha256:bb93a86dcc1512ce1f81dfd84c14e6714261d252997e36eda3e1041a515273ba -->
22+## Mainline
33+44+<!-- mainline-agents-md-version: 8 -->
55+66+This project uses **Mainline** for AI-driven intent tracking and
77+conflict detection. The full agent workflow lives in `AGENTS.md` at
88+the repo root — read that file for the complete contract.
99+1010+Quick reference:
1111+1212+```
1313+mainline status # see your state
1414+1515+# Read team intents for context (do this aggressively):
1616+mainline log --json --limit 30 # recent intents
1717+mainline show <intent_id> --json # full why/decisions/risks
1818+mainline list-proposals --json # what's in flight
1919+2020+# Write your own intent:
2121+mainline start "<goal>" # claim work
2222+mainline append "<what changed>" # after each turn
2323+git add ... && git commit -m ... # commit code
2424+mainline seal --prepare > .ml-cache/seal.json # patch the starter
2525+mainline seal --submit < .ml-cache/seal.json # auto syncs + checks
2626+```
2727+2828+Sync, pin, merge are automatic — do not invoke them.
2929+3030+### If `mainline hooks` is installed for your agent
3131+3232+When hooks are active, the only thing that changes is sessionStart:
3333+the hook runs `mainline sync` + `mainline status` for you and injects
3434+the snapshot into your system context. Every other workflow step
3535+(start / append / commit / seal --prepare / seal --submit / check)
3636+remains agent-driven exactly as `AGENTS.md` describes. Hooks are a
3737+context provider, not a workflow driver.
3838+<!-- mainline:agents:end -->
···11+<!-- mainline:agents:start version=12 checksum=sha256:bb93a86dcc1512ce1f81dfd84c14e6714261d252997e36eda3e1041a515273ba -->
22+## Mainline
33+44+<!-- mainline-agents-md-version: 8 -->
55+66+This project uses **Mainline** for AI-driven intent tracking and
77+conflict detection. The full agent workflow lives in `AGENTS.md` at
88+the repo root — read that file for the complete contract.
99+1010+Quick reference:
1111+1212+```
1313+mainline status # see your state
1414+1515+# Read team intents for context (do this aggressively):
1616+mainline log --json --limit 30 # recent intents
1717+mainline show <intent_id> --json # full why/decisions/risks
1818+mainline list-proposals --json # what's in flight
1919+2020+# Write your own intent:
2121+mainline start "<goal>" # claim work
2222+mainline append "<what changed>" # after each turn
2323+git add ... && git commit -m ... # commit code
2424+mainline seal --prepare > .ml-cache/seal.json # patch the starter
2525+mainline seal --submit < .ml-cache/seal.json # auto syncs + checks
2626+```
2727+2828+Sync, pin, merge are automatic — do not invoke them.
2929+3030+### If `mainline hooks` is installed for your agent
3131+3232+When hooks are active, the only thing that changes is sessionStart:
3333+the hook runs `mainline sync` + `mainline status` for you and injects
3434+the snapshot into your system context. Every other workflow step
3535+(start / append / commit / seal --prepare / seal --submit / check)
3636+remains agent-driven exactly as `AGENTS.md` describes. Hooks are a
3737+context provider, not a workflow driver.
3838+<!-- mainline:agents:end -->
+432
AGENTS.md
···1121123. 更重的 reranker / projection / diversity 控制放后面
113113114114具体 roadmap 见 [docs/ROADMAP.md](/Users/envvar/work/repos/cxs/docs/ROADMAP.md)。
115115+116116+<!-- mainline:agents:start version=12 checksum=sha256:62ee66d15a420f45eb3c1403cffe332072b56e14044597a18ddcc71fa14a0d83 -->
117117+## Mainline
118118+119119+<!-- mainline-agents-md-version: 12 -->
120120+121121+**Mainline is a git-native intent memory layer for AI-assisted engineering.**
122122+It gives coding agents the historical *why* before they inspect the
123123+current *what*.
124124+125125+This project uses Mainline to record the intent behind every AI-driven
126126+change and to surface conflicts between intents before they reach a PR
127127+review. The agent is expected to both **read** team intents (for context)
128128+and **write** its own intent (for the work it's doing). Both halves
129129+matter — intents capture *why* changes were made, which is information
130130+the diff alone cannot give you.
131131+132132+> **v0.3 invariant**: every commit on `main` is in exactly one of three
133133+> states — `covered` (sealed intent claims it), `skipped` (`Mainline-Skip:`
134134+> trailer or matched config pattern), or `uncovered` (neither). Run
135135+> `mainline status` to see the rollup; `mainline gaps` to see uncovered
136136+> commits with rescue suggestions.
137137+138138+### At the start of a task
139139+140140+```
141141+mainline status --json
142142+```
143143+144144+If there is no `active_intent`, start one (use the user's goal verbatim
145145+when possible — it becomes the headline in `mainline log`):
146146+147147+```
148148+mainline start "<short description of the user's goal>" --json
149149+```
150150+151151+### Intent-first workflow (the load-bearing rule)
152152+153153+Before making any non-trivial code change, retrieve relevant intent
154154+context **before** searching the codebase directly.
155155+156156+The default agent order is:
157157+158158+1. `mainline status` — overall state, identity, sync staleness, suggestions.
159159+2. `mainline context --current --json` — historical intents relevant to
160160+ your current branch + active draft + diff vs main.
161161+3. If the task names files: `mainline context --files <path>... --json`.
162162+4. If the task is semantic: `mainline context --query "<task summary>" --json`.
163163+5. Read the returned intents' `summary`, `decisions`, `risks`,
164164+ `anti_patterns`, and `fingerprint`.
165165+6. **Only then** grep / read code to verify against the current
166166+ implementation.
167167+7. Edit.
168168+8. When sealing, reference relevant prior intent IDs in your
169169+ decisions, and record any new `anti_patterns` future agents must
170170+ avoid in this area.
171171+172172+Do not lead with `grep`, `rg`, or broad file reads for non-trivial
173173+changes unless Mainline is unavailable or the task is purely mechanical.
174174+175175+`mainline context` does NOT replace code inspection. It provides the
176176+historical *why* before the agent inspects the current *what*.
177177+178178+**Reading the retrieval output** — every returned intent carries a
179179+`status` field that tells you how to use it RIGHT NOW (distinct from
180180+its lifecycle status):
181181+182182+| status | how to read it |
183183+|---|---|
184184+| `current` | the current effective decision; verify against current code, then apply |
185185+| `superseded` | replaced — read `superseded_by` instead and use this only for context |
186186+| `abandoned` | this approach was tried and abandoned; do not repeat without understanding why |
187187+| `stale` | files have churned or the intent is old; verify decisions still hold |
188188+189189+Each intent also carries:
190190+191191+- `risks` — soft warnings to weigh.
192192+- `anti_patterns` — **hard constraints**. Each one carries a `what`,
193193+ a `why`, and a `severity`. Do not violate them. The retrieval API
194194+ never truncates `anti_patterns`, so if you see one, it is in scope.
195195+- `guidance` — a single-line reminder derived from `status`.
196196+197197+### Pre-edit checklist for agents
198198+199199+Before editing code, answer:
200200+201201+- Did I run `mainline status`?
202202+- Did I run `mainline context --current --json`?
203203+- If files are involved, did I run `mainline context --files ... --json`?
204204+- Did I read the relevant prior decisions and risks?
205205+- Did I verify those intents against the current code?
206206+- Am I about to repeat an abandoned or superseded approach?
207207+208208+### Task priority — when intent-first matters most
209209+210210+| Always mainline-first | Mainline-first preferred | Direct code OK |
211211+|---|---|---|
212212+| architecture changes / refactors | bug fixes | typo / formatting fixes |
213213+| migrations / deletions | new feature additions | one-line obvious syntax fixes |
214214+| auth / billing / data-model / permissions | API behaviour changes | mechanical rename, scoped |
215215+| test-strategy changes | config / CI / release tweaks | user explicitly asks to view ONE file |
216216+| any cross-file change | | |
217217+| user asks "why is this here?" | | |
218218+| user asks "can we delete this?" | | |
219219+| user asks "did we try this before?" | | |
220220+221221+### Read team intents for context (do this aggressively)
222222+223223+Before working on anything non-trivial, scan recent intents for prior
224224+work in the area you're about to touch. Each intent's `summary`
225225+(what / why / decisions / risks / followups) plus `fingerprint`
226226+(subsystems, files_touched, tags) is **strictly richer than the diff** —
227227+it tells you *why* the code looks the way it does, which decisions
228228+were considered and rejected, and what the author flagged as a risk
229229+or follow-up.
230230+231231+```
232232+mainline log --json --limit 30
233233+```
234234+235235+Filter by goal/title keywords matching the user's task. For each
236236+relevant hit, pull the full record:
237237+238238+```
239239+mainline show <intent_id> --json # decisions / risks / fingerprint
240240+mainline trace <intent_id> --json # turn timeline (when each turn
241241+ # was added, how long it took)
242242+```
243243+244244+`show` answers *what* the intent decided. `trace` answers *how* it
245245+unfolded over time — useful when you're trying to understand why a
246246+PR looks the way it does, or whether the agent got stuck and looped.
247247+248248+Before designing a change, also see what is currently in flight so
249249+your work does not collide with someone else's proposed intent:
250250+251251+```
252252+mainline list-proposals --json
253253+```
254254+255255+`mainline context --json` is a quick agent-consumption snapshot of
256256+the same data (current actor, active intent, recent merged) — useful
257257+for orientation but does not replace the targeted log/show calls.
258258+259259+Use this aggressively. The cost is one or two CLI calls; the payoff
260260+is correct architectural decisions and not duplicating someone's
261261+just-finished work.
262262+263263+### Turns and intent history
264264+265265+Turns are a lightweight thinking scaffold used to prepare a good
266266+seal. They are **not** expected to be a real-time activity log.
267267+268268+It is normal for several turns to be recorded together near seal
269269+time, especially when an agent summarizes its work before sealing.
270270+`mainline trace` will surface this honestly via the
271271+`append_turns_recorded_together` flag — that is informational, not a
272272+warning.
273273+274274+Use:
275275+276276+```
277277+mainline show <intent_id> --json
278278+```
279279+280280+to inspect the structured conclusion of an intent: summary,
281281+decisions, risks, and fingerprint.
282282+283283+Use:
284284+285285+```
286286+mainline trace <intent_id> --json
287287+```
288288+289289+to inspect how an intent unfolded over time: start, append, seal,
290290+abandon, or supersede events.
291291+292292+`show` answers: *"What did this intent decide?"*
293293+`trace` answers: *"How did this intent unfold?"*
294294+295295+### While working
296296+297297+Record turns at points that will help you write a good seal — when
298298+a meaningful subtask completes, when you pivot, when a discovery
299299+changes the plan. Many short turns or a few long turns are both
300300+fine; what matters is that the seal author (you, later) has the
301301+material to compose a faithful summary:
302302+303303+```
304304+mainline append "<what changed and why>" --json
305305+```
306306+307307+Turns are append-only. Don't try to amend or delete them — describe
308308+the next state in a new turn.
309309+310310+### When the task is complete
311311+312312+1. Commit your code changes the normal way:
313313+314314+ ```
315315+ git add <files> && git commit -m "<message>"
316316+ ```
317317+318318+2. Ask Mainline to prepare a seal package:
319319+320320+ ```
321321+ mainline seal --prepare --json > .ml-cache/seal.json
322322+ ```
323323+324324+ The package includes a `seal_result_starter` field — a partially-
325325+ filled `SealResult` with the deterministic bits (intent_id,
326326+ fingerprint.files_touched, fingerprint.subsystems) pre-populated.
327327+ Patch in the agent-judgment fields rather than typing the JSON
328328+ from scratch.
329329+330330+ Why `.ml-cache/`? Init writes that directory to `.gitignore`, so
331331+ the temporary seal file stays out of git and does not trip the
332332+ v0.3 worktree-clean snapshot contract on submit.
333333+334334+3. Generate a `SealResult` JSON matching the schema returned by
335335+ `--prepare`. Populate the fingerprint generously — primary subsystem,
336336+ synonyms, parent concepts, related technologies — so phase-1
337337+ conflict detection has signal:
338338+339339+ ```
340340+ "tags": ["auth", "authentication", "security", "jwt", "session"]
341341+ ```
342342+343343+ When the work establishes constraints future agents must respect,
344344+ record them as `anti_patterns` (NOT as `risks`). Each entry MUST
345345+ carry both `what` and `why`; empty `why` is rejected at seal time.
346346+347347+ ```json
348348+ "anti_patterns": [
349349+ {
350350+ "what": "Removing legacy session middleware on /oauth path",
351351+ "why": "OAuth callback handler still requires session state",
352352+ "severity": "high"
353353+ }
354354+ ]
355355+ ```
356356+357357+ Use `risks` for soft warnings the reviewer should weigh; use
358358+ `anti_patterns` for hard constraints the next agent must not
359359+ violate. Anti-patterns are surfaced uncapped in `mainline context`,
360360+ so future agents will always see them.
361361+362362+4. Submit it:
363363+364364+ ```
365365+ mainline seal --submit --json < .ml-cache/seal.json
366366+ ```
367367+368368+ Submit auto-syncs with the team and runs phase-1 conflict detection
369369+ against every other proposed/merged intent. If the JSON response
370370+ carries a `conflicts` array, **surface those conflicts to the user
371371+ verbatim** before continuing. Do not silently move on.
372372+373373+5. (Optional but encouraged) Quality-check the seal:
374374+375375+ ```
376376+ mainline lint <intent_id> --json
377377+ ```
378378+379379+ `lint` runs deterministic checks against the sealed payload —
380380+ empty / boilerplate `what`, missing decisions, decision without
381381+ rationale, missing risks/anti_patterns, broken supersedes refs.
382382+ Errors mean the seal will be hard for future retrieval to use;
383383+ warnings are advisory. Lint is **not** wired into submit, so a
384384+ bad seal still goes through — but a low-quality seal pollutes
385385+ future `mainline context` results, which is the whole loop this
386386+ workflow exists to keep healthy.
387387+388388+### When the user asks you to phase-2 check an intent
389389+390390+Phase 1 is automatic; phase 2 is invoked deliberately when phase 1
391391+flags an overlap (`[check:~]` in `mainline log`) and the user wants a
392392+real semantic judgment.
393393+394394+```
395395+mainline check --prepare --intent <id> --json
396396+```
397397+398398+Read each `judgment_task` in the package, judge whether it is a real
399399+semantic conflict, and submit a `CheckJudgmentResult`:
400400+401401+```
402402+mainline check --submit --json < judgment.json
403403+```
404404+405405+The verdict surfaces in `mainline log`'s `[check:X]` column.
406406+407407+### Optional: agent hooks (opt-in context provider)
408408+409409+If `mainline hooks install <agent>` has been run for your agent
410410+runtime (Cursor today; Codex / Claude Code reserved), the hook layer
411411+runs **two mechanical operations** at session start and injects a
412412+**status snapshot** into your system context — nothing more:
413413+414414+- At `sessionStart` the hook runs `mainline sync` (refreshes the team
415415+ view) and `mainline status` (active intent, proposed count, synced
416416+ head). It feeds that snapshot back to you as system-prompt context
417417+ along with a pointer to this document. You no longer need to run
418418+ `mainline status` as the very first call of a session — it has
419419+ already run.
420420+- At every other lifecycle event (turn start, turn end, subagent
421421+ end, session end) the hook is a **no-op** for your reasoning. It
422422+ fires webhook notifications for external observers (CI dashboards,
423423+ pager integrations) and exits. It does NOT call `mainline start`,
424424+ `mainline append`, `mainline seal --prepare`, or any other command
425425+ that requires deciding what counts as a goal / a meaningful change /
426426+ a fingerprint — those are LLM judgments and you remain the only
427427+ party qualified to make them.
428428+429429+Concretely: every step described above (start when there is real
430430+work, append after each meaningful logical change, commit, seal
431431+--prepare, fill SealResult, seal --submit, surface conflicts) you do
432432+yourself, hooks installed or not. The hook layer is a **context
433433+provider**, not a workflow driver.
434434+435435+Run `mainline hooks status` to confirm whether hooks are wired and
436436+whether `auto_sync_on_session_start` is on (the only mechanical
437437+toggle). Disable it with `mainline hooks disable` if your network
438438+makes the session-start sync painful — you can still drive the rest
439439+of the workflow by hand.
440440+441441+### Hub: human reader surface (you don't run this; you suggest it)
442442+443443+`mainline hub export <dir>` and `mainline hub open` build a static
444444+HTML site over the local synced intent view. It is for **humans**, not
445445+agents — agents use `context` / `show` / `trace` / `gaps`.
446446+447447+You should suggest the hub when the user asks one of:
448448+449449+- *"What's the history of `<file>`?"* → hub's per-file page lists
450450+ every intent that touched it.
451451+- *"Who's been working on what lately?"* → hub's index shows the
452452+ recent intents table; the actor pages give per-author rollups.
453453+- *"Are there any conflicts or risky changes I should review?"* →
454454+ hub's risks page and graph (supersessions, conflicts_with,
455455+ shares_file edges) put the answer one click away.
456456+457457+Concretely:
458458+459459+```
460460+mainline hub open # build + open in the default browser
461461+mainline hub export ./hub-snapshot # write a portable copy elsewhere
462462+```
463463+464464+Both commands default the output to `<os-temp>/mainline-hub/<repo>`
465465+so the static site never enters git.
466466+467467+Hub is read-only and rebuildable from the synced view; it never
468468+modifies repo files outside the user-chosen output directory.
469469+470470+### What you do NOT need to run
471471+472472+- `mainline sync` — runs automatically inside `seal --submit` and
473473+ whenever a fresh-data command (`check`, `pin`) needs it.
474474+- `mainline pin` — runs automatically after every sync; the strategy
475475+ cascade (tree_hash → commit_hash → goal_text) catches GitHub
476476+ squash-merges with near-100 % reliability.
477477+- `mainline merge` — humans merge via the GitHub PR UI; the next
478478+ `mainline sync` auto-pins the squash commit.
479479+480480+### Do not run unless the user explicitly asks
481481+482482+```
483483+mainline pin <intent> <commit> # manual fallback
484484+mainline merge --intent <id> # non-PR pipeline only
485485+mainline init --rewire # repo setup repair
486486+mainline doctor --setup --fix # repo setup repair
487487+```
488488+489489+### Encountering an uncovered commit (v0.3 rescue)
490490+491491+If `mainline status` or `mainline gaps` flags an uncovered commit (one
492492+that landed on main with no intent), pick the **best** path you still
493493+can — ordered by reversibility, cheapest first:
494494+495495+1. **Unpushed** — undo and redo via the proper flow:
496496+497497+ ```
498498+ git reset --soft HEAD^ # un-commit, keep changes
499499+ mainline start "<goal>"
500500+ <continue normal flow>
501501+ ```
502502+503503+2. **Pushed** — backfill an intent that retroactively claims the commit:
504504+505505+ ```
506506+ mainline start "<why this commit was made>" --commits <sha>
507507+ mainline append "<turn-by-turn description, post-hoc>"
508508+ mainline seal --prepare > .ml-cache/seal.json
509509+ <fill .ml-cache/seal.json>
510510+ mainline seal --submit < .ml-cache/seal.json
511511+ ```
512512+513513+ The seal flow auto-pins the new intent to the listed commit on next
514514+ `mainline sync`.
515515+516516+3. **Routine** (chore / format / version bump) — mark as deliberately
517517+ skipped:
518518+519519+ ```
520520+ git commit --amend # add `Mainline-Skip: <reason>` trailer
521521+ ```
522522+523523+ Or add a pattern in `.mainline/config.toml` under `[mainline.skip]`
524524+ so future similar commits classify automatically:
525525+526526+ ```toml
527527+ [mainline.skip]
528528+ patterns = ["^chore: format", "^bump:"]
529529+ ```
530530+531531+4. **Already distributed, regrettably** — accept uncovered. The
532532+ mainline log is a record of reality, not aspiration.
533533+534534+### Seal snapshot contract (v0.3)
535535+536536+`mainline seal --prepare` snapshots the worktree state (HEAD, branch,
537537+clean/dirty/untracked) and persists it. `mainline seal --submit`
538538+validates the live repo against that snapshot — HEAD drift, branch
539539+drift, or dirty worktree all fail by default with a typed error. The
540540+escape hatch is the explicit CLI flag `--allow-dirty`; even then, the
541541+sealed event permanently records `worktree_status` so reviewers see
542542+the audit trail.
543543+544544+Always commit your code BEFORE `mainline seal --prepare`. Untracked
545545+files (planning docs, scratch notes) do **not** enter sealed evidence.
546546+<!-- mainline:agents:end -->
+38
CLAUDE.md
···11+<!-- mainline:agents:start version=12 checksum=sha256:bb93a86dcc1512ce1f81dfd84c14e6714261d252997e36eda3e1041a515273ba -->
22+## Mainline
33+44+<!-- mainline-agents-md-version: 8 -->
55+66+This project uses **Mainline** for AI-driven intent tracking and
77+conflict detection. The full agent workflow lives in `AGENTS.md` at
88+the repo root — read that file for the complete contract.
99+1010+Quick reference:
1111+1212+```
1313+mainline status # see your state
1414+1515+# Read team intents for context (do this aggressively):
1616+mainline log --json --limit 30 # recent intents
1717+mainline show <intent_id> --json # full why/decisions/risks
1818+mainline list-proposals --json # what's in flight
1919+2020+# Write your own intent:
2121+mainline start "<goal>" # claim work
2222+mainline append "<what changed>" # after each turn
2323+git add ... && git commit -m ... # commit code
2424+mainline seal --prepare > .ml-cache/seal.json # patch the starter
2525+mainline seal --submit < .ml-cache/seal.json # auto syncs + checks
2626+```
2727+2828+Sync, pin, merge are automatic — do not invoke them.
2929+3030+### If `mainline hooks` is installed for your agent
3131+3232+When hooks are active, the only thing that changes is sessionStart:
3333+the hook runs `mainline sync` + `mainline status` for you and injects
3434+the snapshot into your system context. Every other workflow step
3535+(start / append / commit / seal --prepare / seal --submit / check)
3636+remains agent-driven exactly as `AGENTS.md` describes. Hooks are a
3737+context provider, not a workflow driver.
3838+<!-- mainline:agents:end -->