triflux 10.50.1 → 10.50.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CLAUDE.md +5 -5
  2. package/README.ko.md +5 -6
  3. package/README.md +5 -6
  4. package/bin/triflux.mjs +3 -72
  5. package/cto/collect.mjs +16 -174
  6. package/cto/current.schema.json +4 -16
  7. package/cto/events.mjs +13 -294
  8. package/cto/hygiene.mjs +26 -346
  9. package/cto/index.mjs +5 -25
  10. package/cto/lake-root.mjs +114 -16
  11. package/cto/status.mjs +31 -78
  12. package/hub/lib/cto-env.mjs +0 -23
  13. package/hub/paths.mjs +0 -6
  14. package/hub/pipeline/index.mjs +0 -190
  15. package/hub/pipeline/transitions.mjs +0 -10
  16. package/hub/reflexion.mjs +1 -84
  17. package/hub/store-adapter.mjs +1 -1
  18. package/hub/team/conductor.mjs +1 -1
  19. package/hub/team/execution-mode.mjs +1 -1
  20. package/hub/team/headless.mjs +1 -1
  21. package/hub/team/psmux.mjs +16 -402
  22. package/hub/team/session.mjs +1 -28
  23. package/hub/team/terminal-opener.mjs +11 -35
  24. package/hud/cli-policy.mjs +1 -5
  25. package/hud/colors.mjs +0 -13
  26. package/hud/constants.mjs +4 -135
  27. package/hud/context-monitor.mjs +3 -64
  28. package/hud/hud-qos-status.mjs +43 -225
  29. package/hud/providers/claude.mjs +4 -16
  30. package/hud/providers/gemini.mjs +7 -698
  31. package/hud/renderers.mjs +40 -294
  32. package/hud/terminal.mjs +1 -63
  33. package/hud/utils.mjs +0 -24
  34. package/package.json +2 -4
  35. package/scripts/__tests__/release-governance.test.mjs +0 -1
  36. package/scripts/lib/gemini-profiles.mjs +1 -1
  37. package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
  38. package/scripts/lib/psmux-info.mjs +18 -49
  39. package/scripts/lib/skill-template.mjs +0 -26
  40. package/scripts/pack.mjs +2 -21
  41. package/scripts/release/bump-version.mjs +8 -31
  42. package/scripts/release/lib.mjs +6 -13
  43. package/scripts/release/prepare.mjs +1 -5
  44. package/scripts/setup.mjs +26 -30
  45. package/scripts/tfx-route-post.mjs +0 -1
  46. package/scripts/tfx-route.sh +0 -9
  47. package/skills/tfx-auto/SKILL.md +1 -3
  48. package/skills/tfx-ship/SKILL.md +1 -1
  49. package/bin/tfx-profile.mjs +0 -7
  50. package/cto/dashboard.mjs +0 -474
  51. package/cto/hygiene-actions.mjs +0 -355
  52. package/cto/hygiene-notify.mjs +0 -170
  53. package/cto/steward.mjs +0 -362
  54. package/hub/codex-compat.mjs +0 -12
  55. package/hub/fullcycle.mjs +0 -113
  56. package/hub/gemini-adapter.mjs +0 -174
  57. package/hub/intent.mjs +0 -321
  58. package/hub/lib/mcp-response-cache.mjs +0 -239
  59. package/hub/lib/path-utils.mjs +0 -165
  60. package/hub/lib/timeline-adapter.mjs +0 -190
  61. package/hub/log-retention.mjs +0 -726
  62. package/hub/pipeline/gates/confidence.mjs +0 -75
  63. package/hub/pipeline/gates/consensus.mjs +0 -106
  64. package/hub/pipeline/gates/index.mjs +0 -9
  65. package/hub/pipeline/gates/selfcheck.mjs +0 -120
  66. package/hub/quality/deslop.mjs +0 -274
  67. package/hub/research.mjs +0 -155
  68. package/hub/role-control-events.mjs +0 -113
  69. package/hub/routing/complexity.mjs +0 -269
  70. package/hub/routing/index.mjs +0 -122
  71. package/hub/routing/q-learning.mjs +0 -387
  72. package/hub/token-mode.mjs +0 -228
  73. package/hub/workers/gemini-worker.mjs +0 -217
  74. package/hud/mission-board.mjs +0 -56
  75. package/hud/providers/cto.mjs +0 -76
  76. package/references/cli-parameter-reference.md +0 -240
  77. package/scripts/__tests__/gen-skill-manifest.test.mjs +0 -81
  78. package/scripts/__tests__/skill-template.test.mjs +0 -293
  79. package/scripts/__tests__/smoke.test.mjs +0 -26
  80. package/scripts/__tests__/tfx-route-bash-node-parity.test.mjs +0 -223
  81. package/scripts/__tests__/tfx-route-node-entry.test.mjs +0 -449
  82. package/scripts/__tests__/tfx-route-phase1-modules.test.mjs +0 -268
  83. package/scripts/cache-buildup.mjs +0 -30
  84. package/scripts/cli-route.sh +0 -3
  85. package/scripts/gen-skill-manifest.mjs +0 -87
  86. package/scripts/lib/agent-json.mjs +0 -27
  87. package/scripts/lib/async.mjs +0 -174
  88. package/scripts/lib/cli-agy.mjs +0 -85
  89. package/scripts/lib/cli-codex.mjs +0 -62
  90. package/scripts/lib/cli-gemini.mjs +0 -37
  91. package/scripts/lib/env.mjs +0 -14
  92. package/scripts/lib/hub.mjs +0 -48
  93. package/scripts/lib/pid.mjs +0 -63
  94. package/scripts/lib/quota.mjs +0 -47
  95. package/scripts/lib/skill-state.mjs +0 -220
  96. package/scripts/lib/team.mjs +0 -75
  97. package/scripts/lib/timeout.mjs +0 -66
  98. package/scripts/lib/tmp.mjs +0 -26
  99. package/scripts/lib/toml.mjs +0 -132
  100. package/scripts/preinstall.mjs +0 -128
  101. package/scripts/session-spawn-helper.mjs +0 -261
  102. package/scripts/test-tfx-route-no-claude-native.mjs +0 -63
  103. package/scripts/tfx-batch-stats.mjs +0 -117
  104. package/scripts/tfx-route.mjs +0 -314
  105. package/scripts/token-snapshot.mjs +0 -696
  106. package/skills/tfx-auto/skill.json +0 -5
  107. package/skills/tfx-doctor/skill.json +0 -5
  108. package/skills/tfx-harness/skill.json +0 -5
  109. package/skills/tfx-live/skill.json +0 -5
  110. package/skills/tfx-remote/skill.json +0 -5
  111. package/skills/tfx-research/skill.json +0 -6
  112. package/skills/tfx-review/skill.json +0 -6
  113. package/skills/tfx-setup/skill.json +0 -5
  114. package/skills/tfx-ship/skill.json +0 -5
  115. package/skills/tfx-wt/skill.json +0 -5
  116. package/tui/codex-profile.mjs +0 -421
  117. package/tui/gemini-profile.mjs +0 -286
package/CLAUDE.md CHANGED
@@ -28,14 +28,14 @@ macOS/Linux는 플랫폼 보호기가 아무 작업도 하지 않게 처리하
28
28
 
29
29
  위 `<psmux-wt>` 룰셋은 Windows 전용이다. macOS/Linux 환경에서 triflux가 터미널/세션을 다루는 방식.
30
30
 
31
- ### terminal-opener.mjs 3단계 대체 경로(`hub/team/terminal-opener.mjs:124~149`)
31
+ ### terminal-opener.mjs 3단계 대체 경로(`hub/team/terminal-opener.mjs` 의 `openCommand`)
32
32
 
33
33
  `openCommand()` 가 순차 평가하는 분기:
34
34
 
35
35
  | 우선순위 | 조건 | 동작 | API |
36
36
  |---------|------|------|-----|
37
37
  | 1 | `platform === "win32"` | `wt-manager.createTab` | `hub/team/wt-manager.mjs` |
38
- | 2 | `isTmuxLikeMux(mux, platform)` (`detectMultiplexer()` → `getMultiplexerType()`/`hasMultiplexer()`/`hasTmux()` + 리터럴 psmux 검사) | `tmux new-window -n <title> <command>` | 셸 직접 호출 |
38
+ | 2 | `isTmuxLikeMux(mux)` (`detectMultiplexer()` → `getMultiplexerType()`/`hasMultiplexer()`/`hasTmux()`) | `tmux new-window -n <title> <command>` | 셸 직접 호출 |
39
39
  | 3 | `platform === "darwin"` (대체 경로) | `open -a Terminal` | macOS `open` 명령 |
40
40
  | — | Linux(멀티플렉서 없음) | 미지원(`false` 반환) | — |
41
41
 
@@ -43,9 +43,9 @@ macOS/Linux는 플랫폼 보호기가 아무 작업도 하지 않게 처리하
43
43
 
44
44
  | 후보 | 필요성 | 이유 |
45
45
  |------|--------|------|
46
- | iTerm2 관리자 | **불필요** | `hub/lib/env-detect.mjs:96`이 `TERM_PROGRAM === "iTerm.app"`을 감지하지만 별도 GUI 패인 조작은 tmux/psmux로 처리한다. 새 창은 `open -a Terminal` 대체 경로로 충분하다. |
47
- | tmux 관리자 | **불필요** | psmux 자체가 tmux 포크다. `terminal-opener.mjs`가 `tmux new-window`로 직접 호출한다. |
48
- | psmux 관리자 | **이미 있음** | `hub/team/psmux.mjs`가 `IS_WINDOWS`/`IS_MAC`으로 플랫폼을 분기한다. |
46
+ | iTerm2 관리자 | **불필요** | `hub/lib/env-detect.mjs:96`이 `TERM_PROGRAM === "iTerm.app"`을 감지하지만 별도 GUI 패인 조작은 tmux로 처리한다. 새 창은 `open -a Terminal` 대체 경로로 충분하다. |
47
+ | tmux 관리자 | **불필요** | `terminal-opener.mjs`가 `tmux new-window`로 직접 호출한다. |
48
+ | psmux 관리자 | **이미 있음** | `hub/team/psmux.mjs`가 Windows에서는 psmux, macOS/Linux에서는 tmux를 사용한다. |
49
49
 
50
50
  참고 사례: OMC(`oh-my-claudecode`)도 운영체제별 관리자를 만들지 않고 Tmux 관리자·워크트리 관리자·Claude 실행기 세 구성 요소로 정리한다. 이 저장소 결정의 외부 근거가 아니라 비교 참고로만 본다.
51
51
 
package/README.ko.md CHANGED
@@ -131,12 +131,11 @@ Codex는 이름 붙은 프로필로 실행한다. 모델 ID는 `~/.codex/<프로
131
131
  | `tfx hub` | 로컬 Hub: `start`, `stop`, `status`, `ensure` |
132
132
  | `tfx mcp` | 관리형 MCP 레지스트리: `list`, `sync`, `add`, `remove` |
133
133
  | `tfx handoff` | 현재 맥락을 다른 세션이나 호스트로 넘길 프롬프트로 묶음 |
134
- | `tfx cto` | 저장소 단위 권위 콘솔: `collect`, `status`, `dashboard`, `hygiene`, `steward`, `event` |
134
+ | `tfx cto` | 저장소 단위 권위 콘솔: `collect`, `status`, `hygiene`(dry-run) |
135
135
  | `tfx review` / `tfx codex-team` | Codex git diff 리뷰 / Codex 주도 팀 모드 |
136
136
  | `tfx stealth-fetch <url>` | cloakbrowser로 URL 하나를 가져옴(JSON을 stdout으로) |
137
137
  | `tfx notion-read`, `tfx why`, `tfx schema`, `tfx list`, `tfx monitor`, `tfx update`, `tfx version` | Notion → Markdown, 커밋 intent 트레일러, CLI 스키마, 설치된 스킬, TUI 모니터, 업데이트, 버전 |
138
138
  | `tfx-live` | 라이브 세션 브리지(`/tfx-live` 스킬과 같은 명령) |
139
- | `tfx-profile` | 대화형 Codex 프로필 관리자 |
140
139
 
141
140
  정확한 인자는 `tfx <명령> --help`나 `tfx schema <명령>`으로 확인한다.
142
141
 
@@ -168,10 +167,10 @@ Claude Code Bash 도구의 600초 제한에 걸리지 않는다. 이후 `--job-s
168
167
  다른 경로로 넘어가지 않고 실패한다. 자세한 내용은
169
168
  [`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
170
169
 
171
- **CTO lake.** `tfx cto`는 저장소에서 일어난 일을 `.triflux/lake/`에 덧붙이기 전용으로 남긴다. 자동
172
- 동작은 기본으로 꺼져 있고 `TFX_CTO_AUTO_COLLECT=1`이나 `TFX_CTO_NORTH_STAR=1`로
173
- 켠다([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)). 예전 트레이 UI는 제거됐다
174
- ([ADR-0022](docs/adr/0022-remove-cto-tray.md)).
170
+ **CTO lake.** `tfx cto collect`로 `.triflux/lake/`의 저장소 스냅샷을 갱신하고,
171
+ `tfx cto status`로 생성 시각과 경과 시간을 확인한다. `tfx cto hygiene --dry-run`는 dry-run 결과를
172
+ 보고한다. 트레이와 쓰이지 않는 CTO 운영 명령은 제거했다
173
+ ([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). 자동 수집은 기본으로 꺼져 있고 `TFX_CTO_AUTO_COLLECT=1` 로 켠다([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)).
175
174
 
176
175
  **원격 호스트.** `/tfx-remote`와 `--remote <host>`는 `~/.config/triflux/hosts.json`
177
176
  (Windows는 `%APPDATA%\triflux\hosts.json`)에서 호스트를 읽는다. `/tfx-remote setup`으로 호스트를
package/README.md CHANGED
@@ -132,12 +132,11 @@ escalation chain: [`.claude/rules/tfx-escalation-chain.md`](.claude/rules/tfx-es
132
132
  | `tfx hub` | Local Hub: `start`, `stop`, `status`, `ensure` |
133
133
  | `tfx mcp` | Managed MCP registry: `list`, `sync`, `add`, `remove` |
134
134
  | `tfx handoff` | Serialize the current context for another session or host |
135
- | `tfx cto` | Repo-local authority console: `collect`, `status`, `dashboard`, `hygiene`, `steward`, `event` |
135
+ | `tfx cto` | Repo-local authority console: `collect`, `status`, `hygiene` (dry-run) |
136
136
  | `tfx review` / `tfx codex-team` | Codex git-diff review / Codex-led team mode |
137
137
  | `tfx stealth-fetch <url>` | Fetch one URL through cloakbrowser (JSON on stdout) |
138
138
  | `tfx notion-read`, `tfx why`, `tfx schema`, `tfx list`, `tfx monitor`, `tfx update`, `tfx version` | Notion → Markdown, commit intent trailers, CLI schemas, installed skills, TUI monitor, update, version |
139
139
  | `tfx-live` | Live session bridge (same as the `/tfx-live` skill) |
140
- | `tfx-profile` | Interactive Codex profile manager |
141
140
 
142
141
  `tfx <command> --help` and `tfx schema <command>` print the exact arguments.
143
142
 
@@ -169,10 +168,10 @@ the `claude agents` panel unless you pass `--no-native-bridge-ui`.
169
168
  removes a CLI from routing; if no allowed CLI is available the route fails instead of silently
170
169
  falling back. Details: [`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
171
170
 
172
- **CTO lake.** `tfx cto` keeps an append-only history of the repo in `.triflux/lake/`. Automatic
173
- behavior is off by default; opt in with `TFX_CTO_AUTO_COLLECT=1` or `TFX_CTO_NORTH_STAR=1`
174
- ([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)). The old tray UI was removed
175
- ([ADR-0022](docs/adr/0022-remove-cto-tray.md)).
171
+ **CTO lake.** Run `tfx cto collect` to refresh the repo snapshot in `.triflux/lake/`, then
172
+ `tfx cto status` to inspect it with its generation time and age. `tfx cto hygiene --dry-run` reports
173
+ dry-run findings. The tray and unused CTO operating commands were removed
174
+ ([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). Automatic collection stays off unless `TFX_CTO_AUTO_COLLECT=1` is set ([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)).
176
175
 
177
176
  **Remote hosts.** `/tfx-remote` and `--remote <host>` read hosts from `~/.config/triflux/hosts.json`
178
177
  (Windows: `%APPDATA%\triflux\hosts.json`). Run `/tfx-remote setup` to add one, then
package/bin/triflux.mjs CHANGED
@@ -476,62 +476,13 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
476
476
  },
477
477
  },
478
478
  cto: {
479
- usage: "tfx cto <collect|status|dashboard|hygiene|steward|event> [options]",
479
+ usage: "tfx cto <collect|status|hygiene> [options]",
480
480
  description: "repo-local authority layer console",
481
481
  subcommands: {
482
482
  collect: "refresh .triflux/lake/current.json from authority sources",
483
483
  status: "print the current authority summary",
484
- dashboard: "render the CTO console dashboard, optionally with --watch",
485
- hygiene: "project CTO hygiene counts and actionable dry-run rows",
486
- steward: {
487
- usage:
488
- "tfx cto steward [--watch] [--max-runs N] [--interval-ms N] [--apply|--dry-run] [--json] [--no-collect]",
489
- description:
490
- "periodically collect and invoke one-shot CTO hygiene without destructive cleanup",
491
- options: [
492
- {
493
- name: "--watch",
494
- type: "boolean",
495
- description:
496
- "run repeatedly until interrupted or --max-runs is reached",
497
- },
498
- {
499
- name: "--max-runs",
500
- type: "number",
501
- description:
502
- "maximum steward iterations; required for --watch --json",
503
- },
504
- {
505
- name: "--interval-ms",
506
- type: "number",
507
- description: "delay between watch iterations in milliseconds",
508
- },
509
- {
510
- name: "--apply",
511
- type: "boolean",
512
- description:
513
- "invoke hygiene apply mode (ledger acknowledgement only)",
514
- },
515
- {
516
- name: "--dry-run",
517
- type: "boolean",
518
- description: "invoke hygiene dry-run mode (default)",
519
- },
520
- {
521
- name: "--json",
522
- type: "boolean",
523
- description:
524
- "print one finite steward JSON summary object; with --watch requires --max-runs",
525
- },
526
- {
527
- name: "--no-collect",
528
- type: "boolean",
529
- description: "skip the collect pass before hygiene",
530
- },
531
- ],
532
- },
533
- event:
534
- "append wrapper lineage events: context-save, context-restore, pr-created, pr-merged-or-closed",
484
+ hygiene:
485
+ "project CTO hygiene counts and actionable dry-run rows (--dry-run)",
535
486
  },
536
487
  },
537
488
  multi: {
@@ -2583,26 +2534,6 @@ async function cmdDoctor(options = {}) {
2583
2534
  });
2584
2535
  warn("Codex 레이트 리밋 캐시 재생성 실패");
2585
2536
  }
2586
- try {
2587
- execFileSync(
2588
- process.execPath,
2589
- [hudScript, "--refresh-gemini-quota"],
2590
- { timeout: 15000, stdio: "ignore", windowsHide: true },
2591
- );
2592
- report.actions.push({
2593
- type: "rebuild",
2594
- name: "gemini-quota-cache",
2595
- status: "ok",
2596
- });
2597
- ok("Gemini 쿼터 캐시 재생성됨");
2598
- } catch {
2599
- report.actions.push({
2600
- type: "rebuild",
2601
- name: "gemini-quota-cache",
2602
- status: "failed",
2603
- });
2604
- warn("Gemini 쿼터 캐시 재생성 실패");
2605
- }
2606
2537
  }
2607
2538
  try {
2608
2539
  const { buildAll } = await import("../scripts/cache-warmup.mjs");
package/cto/collect.mjs CHANGED
@@ -1,23 +1,23 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import {
3
- appendFileSync,
4
- closeSync,
5
3
  existsSync,
6
4
  mkdirSync,
7
- openSync,
8
5
  readdirSync,
9
6
  readFileSync,
10
- renameSync,
11
7
  statSync,
12
- unlinkSync,
13
- writeFileSync,
14
8
  } from "node:fs";
15
9
  import { homedir } from "node:os";
16
- import { basename, dirname, join, relative } from "node:path";
10
+ import { join, relative } from "node:path";
17
11
  import { fileURLToPath } from "node:url";
18
12
 
19
13
  import { renderBrief } from "./brief.mjs";
20
- import { resolveLakeRootDir } from "./lake-root.mjs";
14
+ import { appendCtoEvent } from "./events.mjs";
15
+ import {
16
+ firstExisting,
17
+ readJsonLines,
18
+ resolveLakeRootDir,
19
+ writeAtomic,
20
+ } from "./lake-root.mjs";
21
21
 
22
22
  const SCHEMA_VERSION = "cto-lake.v1";
23
23
  const SOURCE_REGISTRY = [
@@ -69,24 +69,6 @@ const SOURCE_REGISTRY = [
69
69
  probe: ".omx/handoffs/*",
70
70
  enabled: true,
71
71
  },
72
- {
73
- id: "session_vault",
74
- kind: "optional-durable-ref",
75
- probe: ".triflux/session-vault.json, sessions_v2.db, or .session-vault",
76
- enabled: true,
77
- },
78
- {
79
- id: "agy",
80
- kind: "optional-durable-ref",
81
- probe: ".agy/state.json or .agy/refs.json",
82
- enabled: true,
83
- },
84
- {
85
- id: "gbrain",
86
- kind: "optional-durable-ref",
87
- probe: ".gbrain/refs.json or .gbrain/config.json",
88
- enabled: true,
89
- },
90
72
  ];
91
73
  const SOURCE_IDS = SOURCE_REGISTRY.map((source) => source.id);
92
74
 
@@ -104,10 +86,6 @@ function isoNow(opts) {
104
86
  return value.toISOString();
105
87
  }
106
88
 
107
- function sleep(ms) {
108
- return new Promise((resolve) => setTimeout(resolve, ms));
109
- }
110
-
111
89
  function sourceState(available, status, detail, collectedAt) {
112
90
  return {
113
91
  available,
@@ -130,25 +108,6 @@ function readJson(filePath) {
130
108
  return JSON.parse(readFileSync(filePath, "utf8"));
131
109
  }
132
110
 
133
- function readJsonLines(filePath, limit = 20) {
134
- if (!existsSync(filePath)) return [];
135
- const lines = readFileSync(filePath, "utf8")
136
- .split(/\r?\n/u)
137
- .map((line) => line.trim())
138
- .filter(Boolean);
139
- return lines.slice(-limit).flatMap((line) => {
140
- try {
141
- return [JSON.parse(line)];
142
- } catch {
143
- return [];
144
- }
145
- });
146
- }
147
-
148
- function findFirstExisting(candidates) {
149
- return candidates.find((candidate) => existsSync(candidate)) || null;
150
- }
151
-
152
111
  function listDirFiles(dirPath, rootDir, limit = 10) {
153
112
  if (!existsSync(dirPath)) return [];
154
113
  try {
@@ -237,7 +196,7 @@ function collectJsonArtifact(
237
196
  collectedAt,
238
197
  summarize = safeSummary,
239
198
  ) {
240
- const filePath = findFirstExisting(candidates);
199
+ const filePath = firstExisting(candidates);
241
200
  if (!filePath) {
242
201
  return missingSource(
243
202
  "no_shell_readable_artifact",
@@ -429,59 +388,6 @@ function collectHandoffs(rootDir, collectedAt) {
429
388
  );
430
389
  }
431
390
 
432
- function collectDurableRefs(
433
- id,
434
- candidates,
435
- rootDir,
436
- collectedAt,
437
- unavailableStatus = "no_shell_readable_artifact",
438
- ) {
439
- const filePath = findFirstExisting(candidates);
440
- if (!filePath) {
441
- return missingSource(
442
- unavailableStatus,
443
- `${id} exposes no shell-readable durable artifact`,
444
- collectedAt,
445
- );
446
- }
447
- try {
448
- const stat = statSync(filePath);
449
- if (stat.isDirectory()) {
450
- return sourceState(
451
- true,
452
- "ok",
453
- {
454
- path: relPath(rootDir, filePath),
455
- files: listDirFiles(filePath, rootDir, 10),
456
- },
457
- collectedAt,
458
- );
459
- }
460
- const parsed = basename(filePath).endsWith(".json")
461
- ? readJson(filePath)
462
- : readFileSync(filePath, "utf8");
463
- return sourceState(
464
- true,
465
- "ok",
466
- {
467
- path: relPath(rootDir, filePath),
468
- summary: safeSummary(parsed),
469
- },
470
- collectedAt,
471
- );
472
- } catch (error) {
473
- return sourceState(
474
- true,
475
- "read_error",
476
- {
477
- path: relPath(rootDir, filePath),
478
- error: error?.message || `${id} read failed`,
479
- },
480
- collectedAt,
481
- );
482
- }
483
- }
484
-
485
391
  function buildSummary(current) {
486
392
  const repo = current.repo;
487
393
  const omxGoals = current.sources.ultragoal_omx.detail?.active_goals || [];
@@ -633,48 +539,6 @@ function validateCurrent(current) {
633
539
  }
634
540
  }
635
541
 
636
- function writeAtomic(filePath, body) {
637
- mkdirSync(dirname(filePath), { recursive: true });
638
- const tmpPath = join(
639
- dirname(filePath),
640
- `.${basename(filePath)}.${process.pid}.${Date.now()}.tmp`,
641
- );
642
- writeFileSync(tmpPath, body, "utf8");
643
- renameSync(tmpPath, filePath);
644
- }
645
-
646
- async function appendLedgerEvent(lakeRoot, event, stderr) {
647
- mkdirSync(lakeRoot, { recursive: true });
648
- const ledgerPath = join(lakeRoot, "ledger.jsonl");
649
- const lockPath = join(lakeRoot, "ledger.jsonl.lock");
650
- let fd = null;
651
- for (let attempt = 0; attempt < 3; attempt += 1) {
652
- try {
653
- fd = openSync(lockPath, "wx", 0o600);
654
- break;
655
- } catch (error) {
656
- if (error?.code !== "EEXIST") throw error;
657
- if (attempt < 2) await sleep(100);
658
- }
659
- }
660
- if (fd === null) {
661
- stderr.write(
662
- "[tfx cto collect] warning: ledger lock timeout; skipped ledger append\n",
663
- );
664
- return false;
665
- }
666
-
667
- try {
668
- appendFileSync(ledgerPath, `${JSON.stringify(event)}\n`, "utf8");
669
- return true;
670
- } finally {
671
- closeSync(fd);
672
- try {
673
- unlinkSync(lockPath);
674
- } catch {}
675
- }
676
- }
677
-
678
542
  function collectSources(rootDir, collectedAt, execFileSyncFn, opts = {}) {
679
543
  const home = homedir();
680
544
  const hostArtifactCandidates =
@@ -748,34 +612,6 @@ function collectSources(rootDir, collectedAt, execFileSyncFn, opts = {}) {
748
612
  collectedAt,
749
613
  ),
750
614
  handoffs: collectHandoffs(rootDir, collectedAt),
751
- session_vault: collectDurableRefs(
752
- "session_vault",
753
- [
754
- join(rootDir, ".triflux", "session-vault.json"),
755
- join(rootDir, "sessions_v2.db"),
756
- join(rootDir, ".session-vault"),
757
- ],
758
- rootDir,
759
- collectedAt,
760
- "not_shell_collectable",
761
- ),
762
- agy: collectDurableRefs(
763
- "agy",
764
- [join(rootDir, ".agy", "state.json"), join(rootDir, ".agy", "refs.json")],
765
- rootDir,
766
- collectedAt,
767
- "not_shell_collectable",
768
- ),
769
- gbrain: collectDurableRefs(
770
- "gbrain",
771
- [
772
- join(rootDir, ".gbrain", "refs.json"),
773
- join(rootDir, ".gbrain", "config.json"),
774
- ],
775
- rootDir,
776
- collectedAt,
777
- "not_shell_collectable",
778
- ),
779
615
  };
780
616
 
781
617
  return { repo: git.repo, sources };
@@ -786,6 +622,12 @@ function hasFlag(args, flag) {
786
622
  }
787
623
 
788
624
  export async function runCollect(args = [], opts = {}) {
625
+ if (hasFlag(args, "--help")) {
626
+ (opts.stdout || process.stdout).write(
627
+ "Usage: tfx cto collect [--json]\nRefresh the CTO lake from durable authority sources.\n",
628
+ );
629
+ return;
630
+ }
789
631
  const rootDir = opts.rootDir || resolveLakeRootDir(process.cwd());
790
632
  const lakeRoot = opts.lakeRoot || join(rootDir, ".triflux", "lake");
791
633
  const stdout = opts.stdout || process.stdout;
@@ -822,7 +664,7 @@ export async function runCollect(args = [], opts = {}) {
822
664
  sources_json: "sources.json",
823
665
  },
824
666
  };
825
- const appended = await appendLedgerEvent(lakeRoot, event, stderr);
667
+ const { appended } = await appendCtoEvent(lakeRoot, event, { stderr });
826
668
  if (appended) current.ledger_tail = readLedgerTail(lakeRoot);
827
669
 
828
670
  validateCurrent(current);
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://triflux.local/schemas/cto/current.schema.json",
4
4
  "title": "Triflux CTO Lake Current Authority State",
5
- "description": "Contract for .triflux/lake/current.json. .triflux/lake is a triflux-owned, namespace-neutral cross-agent rollup for durable artifacts across Claude, Codex/OMX, OMC, AGY, gstack, gbrain, and Triflux state. It reads and aggregates .omx/ultragoal and .omc/ultragoal execution ledgers without replacing or duplicating their authority. ledger.jsonl and sources.json companion contracts are documented in $defs.",
5
+ "description": "Contract for .triflux/lake/current.json. .triflux/lake is a triflux-owned, namespace-neutral cross-agent rollup for durable artifacts across Claude, Codex/OMX, OMC, and Triflux state. It reads and aggregates .omx/ultragoal and .omc/ultragoal execution ledgers without replacing or duplicating their authority. ledger.jsonl and sources.json companion contracts are documented in $defs.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "sources": {
46
46
  "type": "object",
47
- "description": "Durable artifact source states only. Live Claude /goal state and AGY internal state are not shell-collectable; a source reports available:false when no shell-readable durable artifact exists.",
47
+ "description": "Durable artifact source states only. A source reports available:false when no shell-readable durable artifact exists.",
48
48
  "additionalProperties": false,
49
49
  "required": [
50
50
  "git",
@@ -54,10 +54,7 @@
54
54
  "tfx_synapse",
55
55
  "ultragoal_omx",
56
56
  "ultragoal_omc",
57
- "handoffs",
58
- "session_vault",
59
- "agy",
60
- "gbrain"
57
+ "handoffs"
61
58
  ],
62
59
  "properties": {
63
60
  "git": {
@@ -83,21 +80,12 @@
83
80
  },
84
81
  "handoffs": {
85
82
  "$ref": "#/$defs/sourceState"
86
- },
87
- "session_vault": {
88
- "$ref": "#/$defs/sourceState"
89
- },
90
- "agy": {
91
- "$ref": "#/$defs/sourceState"
92
- },
93
- "gbrain": {
94
- "$ref": "#/$defs/sourceState"
95
83
  }
96
84
  }
97
85
  },
98
86
  "summary": {
99
87
  "type": "object",
100
- "description": "Human- and dashboard-oriented synthesized authority summary derived from durable artifacts and live Triflux synapse session status. Phase 1 may add source-derived fields here without changing the top-level contract.",
88
+ "description": "Human-oriented synthesized authority summary derived from durable artifacts and live Triflux synapse session status. Phase 1 may add source-derived fields here without changing the top-level contract.",
101
89
  "additionalProperties": true
102
90
  },
103
91
  "ledger_tail": {