triflux 10.30.1 → 10.31.0
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.
- package/hub/server.mjs +11 -0
- package/hub/team/synapse-registry.mjs +83 -1
- package/package.json +1 -1
- package/scripts/tfx-route.sh +99 -0
- package/skills/tfx-auto/SKILL.md +33 -1
package/hub/server.mjs
CHANGED
|
@@ -2054,6 +2054,16 @@ export async function startHub({
|
|
|
2054
2054
|
}, 60000);
|
|
2055
2055
|
sessionTimer.unref();
|
|
2056
2056
|
|
|
2057
|
+
const synapsePruneTimer = setInterval(() => {
|
|
2058
|
+
try {
|
|
2059
|
+
const { count } = synapseRegistry.pruneExpired();
|
|
2060
|
+
if (count > 0) hubLog.info({ count }, "synapse.prune");
|
|
2061
|
+
} catch (err) {
|
|
2062
|
+
hubLog.warn({ err }, "synapse.prune_failed");
|
|
2063
|
+
}
|
|
2064
|
+
}, 60000);
|
|
2065
|
+
synapsePruneTimer.unref();
|
|
2066
|
+
|
|
2057
2067
|
// 고아 node.exe 프로세스 + stale spawn 세션 주기적 정리 (5분마다)
|
|
2058
2068
|
// TFX_DISABLE_ORPHAN_CLEANUP=1 로 비활성 (active SSH/swarm 세션 보호 회피용 hotfix gate)
|
|
2059
2069
|
const orphanCleanupTimer = setInterval(
|
|
@@ -2288,6 +2298,7 @@ export async function startHub({
|
|
|
2288
2298
|
router.stopSweeper();
|
|
2289
2299
|
clearInterval(hitlTimer);
|
|
2290
2300
|
clearInterval(sessionTimer);
|
|
2301
|
+
clearInterval(synapsePruneTimer);
|
|
2291
2302
|
clearInterval(rateLimitTimer);
|
|
2292
2303
|
clearInterval(orphanCleanupTimer);
|
|
2293
2304
|
if (idleTimer) {
|
|
@@ -5,6 +5,7 @@ const DEFAULT_LOCAL_HEARTBEAT_INTERVAL_MS = 5_000;
|
|
|
5
5
|
const DEFAULT_LOCAL_TIMEOUT_MS = 30_000;
|
|
6
6
|
const DEFAULT_REMOTE_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
7
7
|
const DEFAULT_REMOTE_TIMEOUT_MS = 90_000;
|
|
8
|
+
const DEFAULT_EXPIRE_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
8
9
|
// Interactive (Claude/Codex) sessions idle for long stretches; a 30s local
|
|
9
10
|
// timeout produces stale false-positives. 5-minute TTL + an `idle` state
|
|
10
11
|
// distinguishes "alive but inactive" from "presumed dead".
|
|
@@ -133,6 +134,7 @@ export function createSynapseRegistry(opts = {}) {
|
|
|
133
134
|
remoteTimeoutMs = DEFAULT_REMOTE_TIMEOUT_MS,
|
|
134
135
|
interactiveHeartbeatIntervalMs = DEFAULT_INTERACTIVE_HEARTBEAT_INTERVAL_MS,
|
|
135
136
|
interactiveTimeoutMs = DEFAULT_INTERACTIVE_TIMEOUT_MS,
|
|
137
|
+
expireTimeoutMs = DEFAULT_EXPIRE_TIMEOUT_MS,
|
|
136
138
|
} = opts;
|
|
137
139
|
|
|
138
140
|
const sessions = new Map();
|
|
@@ -355,10 +357,89 @@ export function createSynapseRegistry(opts = {}) {
|
|
|
355
357
|
return true;
|
|
356
358
|
}
|
|
357
359
|
|
|
360
|
+
// stale/expired 세션이 expireTimeoutMs 넘게 누적되면 Map에서 제거.
|
|
361
|
+
// live(active/idle)는 lastHeartbeat가 오래돼도 보존 — git-preflight dirty-file 가드가 의존.
|
|
362
|
+
function pruneExpired(opts2 = {}) {
|
|
363
|
+
if (destroyed) return { removed: [], count: 0 };
|
|
364
|
+
|
|
365
|
+
const cutoff =
|
|
366
|
+
typeof opts2.olderThanMs === "number"
|
|
367
|
+
? opts2.olderThanMs
|
|
368
|
+
: expireTimeoutMs;
|
|
369
|
+
const removed = [];
|
|
370
|
+
const currentTime = now();
|
|
371
|
+
|
|
372
|
+
for (const [sessionId, session] of sessions) {
|
|
373
|
+
if (
|
|
374
|
+
isLiveStatus(session.status) ||
|
|
375
|
+
currentTime - session.lastHeartbeat <= cutoff
|
|
376
|
+
) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
stopMonitor(sessionId);
|
|
381
|
+
sessions.delete(sessionId);
|
|
382
|
+
removed.push(sessionId);
|
|
383
|
+
notifyRemoved(session);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (removed.length > 0) persist();
|
|
387
|
+
return { removed, count: removed.length };
|
|
388
|
+
}
|
|
389
|
+
|
|
358
390
|
function heartbeat(sessionId, partialMeta = null) {
|
|
359
391
|
const normalized = normalizeSessionId(sessionId);
|
|
360
392
|
const session = sessions.get(normalized);
|
|
361
|
-
if (!session)
|
|
393
|
+
if (!session) {
|
|
394
|
+
// self-heal은 위치 정보(worktreePath 또는 cwd)가 있는 heartbeat에서만
|
|
395
|
+
// 발동한다. UserPromptSubmit heartbeat는 항상 worktreePath를 보내므로
|
|
396
|
+
// SessionStart register POST drop 복구 경로는 커버되고, taskSummary만 담은
|
|
397
|
+
// 빈약한 heartbeat로 위치 없는 가비지 세션이 register 되는 것은 막는다
|
|
398
|
+
// (그 경우는 기존대로 false → 400/heartbeat failed).
|
|
399
|
+
const hasLocator =
|
|
400
|
+
partialMeta &&
|
|
401
|
+
typeof partialMeta === "object" &&
|
|
402
|
+
!Array.isArray(partialMeta) &&
|
|
403
|
+
((typeof partialMeta.worktreePath === "string" &&
|
|
404
|
+
partialMeta.worktreePath.length > 0) ||
|
|
405
|
+
(typeof partialMeta.cwd === "string" && partialMeta.cwd.length > 0));
|
|
406
|
+
const canSelfHeal = Boolean(normalized) && hasLocator;
|
|
407
|
+
if (!canSelfHeal) return false;
|
|
408
|
+
|
|
409
|
+
// 미등록 세션 heartbeat = SessionStart register POST가 drop된 경우.
|
|
410
|
+
// self-heal로 register 복구(영구 미등록 방지).
|
|
411
|
+
//
|
|
412
|
+
// sessionKind는 명시값이 없으면 "interactive"로 본다. 실제 복구 경로인
|
|
413
|
+
// heartbeatInteractiveSession은 worktreePath/host/taskSummary만 보내고
|
|
414
|
+
// sessionKind는 생략하는데, sanitizeSession 기본값("headless")으로 떨어지면
|
|
415
|
+
// 복구된 Claude 세션이 30초 local timeout을 받아 idle/5분 TTL을 잃고 곧장
|
|
416
|
+
// stale로 전이돼 getActive()/peer discovery에서 사라진다(이 PRD가 고치려던
|
|
417
|
+
// 원래 증상의 재현). heartbeat 기반 liveness로 살아나는 세션은 interactive다.
|
|
418
|
+
const sanitizedKind =
|
|
419
|
+
typeof partialMeta.sessionKind === "string"
|
|
420
|
+
? partialMeta.sessionKind
|
|
421
|
+
: "interactive";
|
|
422
|
+
const healed = sanitizeSession(
|
|
423
|
+
{
|
|
424
|
+
...partialMeta,
|
|
425
|
+
sessionId: normalized,
|
|
426
|
+
sessionKind: sanitizedKind,
|
|
427
|
+
status: "active",
|
|
428
|
+
lastHeartbeat: now(),
|
|
429
|
+
},
|
|
430
|
+
normalized,
|
|
431
|
+
);
|
|
432
|
+
if (!healed) return false;
|
|
433
|
+
|
|
434
|
+
sessions.set(normalized, healed);
|
|
435
|
+
startMonitor(normalized);
|
|
436
|
+
persist();
|
|
437
|
+
emitter?.emit("synapse.session.started", {
|
|
438
|
+
sessionId: normalized,
|
|
439
|
+
session: cloneSession(healed),
|
|
440
|
+
});
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
362
443
|
|
|
363
444
|
const wasRemote = session.isRemote;
|
|
364
445
|
const updated = { ...session, lastHeartbeat: now(), status: "active" };
|
|
@@ -492,6 +573,7 @@ export function createSynapseRegistry(opts = {}) {
|
|
|
492
573
|
return Object.freeze({
|
|
493
574
|
register,
|
|
494
575
|
unregister,
|
|
576
|
+
pruneExpired,
|
|
495
577
|
heartbeat,
|
|
496
578
|
getActive,
|
|
497
579
|
getAll,
|
package/package.json
CHANGED
package/scripts/tfx-route.sh
CHANGED
|
@@ -464,6 +464,85 @@ prepend_codex_north_star() {
|
|
|
464
464
|
rm -f "$tmp_file" 2>/dev/null || true
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
+
# prepend_skill: opt-in 으로 등록된 스킬의 SKILL.md 본문을 프롬프트 앞에 주입한다.
|
|
468
|
+
# 활성 조건은 TFX_INJECT_SKILL env (tfx-auto 가 --skill <name> 으로 set). 미설정이면
|
|
469
|
+
# 현행 동작 그대로 (no-op). CLI-agnostic — codex/agy 레인에서 동일하게 재사용한다.
|
|
470
|
+
# 전달은 prepend_codex_north_star 와 동일하게 printf/cat → temp file 만 사용하므로
|
|
471
|
+
# $ / 백슬래시(₩) 같은 특수문자를 셸 재확장 없이 리터럴로 보존한다 (parse-safe).
|
|
472
|
+
# 스킬 경로: ${TFX_SKILLS_DIR:-<repo>/skills}/<name>/SKILL.md.
|
|
473
|
+
prepend_skill() {
|
|
474
|
+
local prompt="${1:-}"
|
|
475
|
+
local skill_name="${TFX_INJECT_SKILL:-}"
|
|
476
|
+
if [[ -z "$skill_name" ]]; then
|
|
477
|
+
printf '%s' "$prompt"
|
|
478
|
+
return 0
|
|
479
|
+
fi
|
|
480
|
+
# 경로 탈출 방지: skill_name 은 skills/ 하위 단일 디렉토리 이름만 허용한다.
|
|
481
|
+
if [[ "$skill_name" == *"/"* || "$skill_name" == *".."* ]]; then
|
|
482
|
+
echo "[tfx-route] WARNING: TFX_INJECT_SKILL='${skill_name}' 에 경로 구분자/.. 포함 — 주입 거부" >&2
|
|
483
|
+
printf '%s' "$prompt"
|
|
484
|
+
return 0
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
local skills_dir="${TFX_SKILLS_DIR:-}"
|
|
488
|
+
if [[ -z "$skills_dir" ]]; then
|
|
489
|
+
skills_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." 2>/dev/null && pwd)/skills"
|
|
490
|
+
fi
|
|
491
|
+
local skill_file="${skills_dir}/${skill_name}/SKILL.md"
|
|
492
|
+
if [[ ! -r "$skill_file" ]]; then
|
|
493
|
+
echo "[tfx-route] WARNING: TFX_INJECT_SKILL='${skill_name}' 스킬을 찾지 못함 ($skill_file) — 주입 생략" >&2
|
|
494
|
+
printf '%s' "$prompt"
|
|
495
|
+
return 0
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
local tmp_file
|
|
499
|
+
tmp_file="$(mktemp "${TFX_TMP:-${TMPDIR:-/tmp}}/tfx-skill-prompt.XXXXXX" 2>/dev/null)" || {
|
|
500
|
+
printf '%s' "$prompt"
|
|
501
|
+
return 0
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if ! {
|
|
505
|
+
printf '%s\n' "--- SKILL: ${skill_name} (apply this methodology to the task below) ---"
|
|
506
|
+
cat "$skill_file"
|
|
507
|
+
if [[ -s "$skill_file" ]]; then
|
|
508
|
+
local last_byte
|
|
509
|
+
last_byte="$(tail -c 1 "$skill_file" 2>/dev/null || true)"
|
|
510
|
+
[[ -n "$last_byte" ]] && printf '\n'
|
|
511
|
+
fi
|
|
512
|
+
printf '%s\n' "--- END SKILL ---"
|
|
513
|
+
printf '%s' "$prompt"
|
|
514
|
+
} > "$tmp_file"; then
|
|
515
|
+
rm -f "$tmp_file" 2>/dev/null || true
|
|
516
|
+
printf '%s' "$prompt"
|
|
517
|
+
return 0
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
cat "$tmp_file" 2>/dev/null || printf '%s' "$prompt"
|
|
521
|
+
rm -f "$tmp_file" 2>/dev/null || true
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
# append_agy_anti_overclaim: agy(Gemini 3.x) 레인 전용 완료/grounding 규율 블록을
|
|
525
|
+
# 프롬프트 *뒤*에 붙인다. Gemini 3.x 는 과신(overconfidence) outlier 라 거짓 완료
|
|
526
|
+
# 주장·환각이 잦다 (AA-Omniscience 실증). 부정 제약은 Gemini 3 공식 가이드 권고대로
|
|
527
|
+
# 프롬프트 END 에 배치하고, blanket "do not guess" 대신 "주어진 context 로 추론 +
|
|
528
|
+
# 정확한 abstention 우선" 형태로 적는다 (open-ended negative 는 역효과). TFX_AGY_ANTI_OVERCLAIM=0 으로 opt-out.
|
|
529
|
+
append_agy_anti_overclaim() {
|
|
530
|
+
local prompt="${1:-}"
|
|
531
|
+
case "${TFX_AGY_ANTI_OVERCLAIM:-1}" in
|
|
532
|
+
0|false|FALSE|off|OFF|no|NO)
|
|
533
|
+
printf '%s' "$prompt"
|
|
534
|
+
return 0
|
|
535
|
+
;;
|
|
536
|
+
esac
|
|
537
|
+
|
|
538
|
+
printf '%s' "$prompt"
|
|
539
|
+
printf '\n\n%s\n' "--- COMPLETION & GROUNDING DISCIPLINE (apply strictly) ---"
|
|
540
|
+
printf '%s\n' "- Claim done/fixed/passing ONLY after running the check in this turn and seeing its output. With no fresh evidence, report what is still unverified instead of asserting success."
|
|
541
|
+
printf '%s\n' "- Ground every answer in the provided context and the actual repository. If something is not verifiable from what you can see, say 'No Info / 확인 불가' and stop rather than inventing plausible-but-unconfirmed details."
|
|
542
|
+
printf '%s\n' "- Use the provided context for deductions and prefer accurate abstention over confident guessing."
|
|
543
|
+
printf '%s' "--- END DISCIPLINE ---"
|
|
544
|
+
}
|
|
545
|
+
|
|
467
546
|
read_probe_state() {
|
|
468
547
|
local pid="$1"
|
|
469
548
|
local state_file="${TFX_PROBE_STATE_FILE:-${TFX_PROBE_DIR}/${pid}.json}"
|
|
@@ -2678,6 +2757,14 @@ FALLBACK_EOF
|
|
|
2678
2757
|
_codex_full_prompt_with_sentinel="$(prepend_codex_north_star "$FULL_PROMPT"; printf '%s' "$_codex_prompt_sentinel")"
|
|
2679
2758
|
FULL_PROMPT="${_codex_full_prompt_with_sentinel%"$_codex_prompt_sentinel"}"
|
|
2680
2759
|
fi
|
|
2760
|
+
# Opt-in 스킬 주입 (TFX_INJECT_SKILL). north-star 와 동일한 sentinel 패턴으로
|
|
2761
|
+
# trailing newline 을 보존한다. 미설정이면 prepend_skill 이 no-op.
|
|
2762
|
+
if [[ -n "${TFX_INJECT_SKILL:-}" ]]; then
|
|
2763
|
+
local _codex_skill_sentinel="__TFX_CODEX_SKILL_END_${$}_${RANDOM}__"
|
|
2764
|
+
local _codex_skill_prompt
|
|
2765
|
+
_codex_skill_prompt="$(prepend_skill "$FULL_PROMPT"; printf '%s' "$_codex_skill_sentinel")"
|
|
2766
|
+
FULL_PROMPT="${_codex_skill_prompt%"$_codex_skill_sentinel"}"
|
|
2767
|
+
fi
|
|
2681
2768
|
codex_transport_effective="exec"
|
|
2682
2769
|
if [[ "$TFX_CODEX_TRANSPORT" != "exec" ]]; then
|
|
2683
2770
|
run_codex_mcp "$FULL_PROMPT" "$use_tee" || exit_code=$?
|
|
@@ -2782,6 +2869,18 @@ EOF
|
|
|
2782
2869
|
_agy_full_prompt_with_sentinel="$(prepend_codex_north_star "$FULL_PROMPT"; printf '%s' "$_agy_prompt_sentinel")"
|
|
2783
2870
|
FULL_PROMPT="${_agy_full_prompt_with_sentinel%"$_agy_prompt_sentinel"}"
|
|
2784
2871
|
fi
|
|
2872
|
+
# Opt-in 스킬 주입 (TFX_INJECT_SKILL) — codex 레인과 동일.
|
|
2873
|
+
if [[ -n "${TFX_INJECT_SKILL:-}" ]]; then
|
|
2874
|
+
local _agy_skill_sentinel="__TFX_AGY_SKILL_END_${$}_${RANDOM}__"
|
|
2875
|
+
local _agy_skill_prompt
|
|
2876
|
+
_agy_skill_prompt="$(prepend_skill "$FULL_PROMPT"; printf '%s' "$_agy_skill_sentinel")"
|
|
2877
|
+
FULL_PROMPT="${_agy_skill_prompt%"$_agy_skill_sentinel"}"
|
|
2878
|
+
fi
|
|
2879
|
+
# agy(Gemini 3.x) anti-overclaim 규율 블록을 프롬프트 END 에 append. opt-out: TFX_AGY_ANTI_OVERCLAIM=0.
|
|
2880
|
+
local _agy_aoc_sentinel="__TFX_AGY_AOC_END_${$}_${RANDOM}__"
|
|
2881
|
+
local _agy_aoc_prompt
|
|
2882
|
+
_agy_aoc_prompt="$(append_agy_anti_overclaim "$FULL_PROMPT"; printf '%s' "$_agy_aoc_sentinel")"
|
|
2883
|
+
FULL_PROMPT="${_agy_aoc_prompt%"$_agy_aoc_sentinel"}"
|
|
2785
2884
|
run_antigravity_exec "$FULL_PROMPT" "$use_tee" || exit_code=$?
|
|
2786
2885
|
if [[ "$exit_code" -ne 0 && "$exit_code" -ne 124 ]]; then
|
|
2787
2886
|
local agy_stderr_bytes=0
|
package/skills/tfx-auto/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ triggers:
|
|
|
28
28
|
- spec-panel
|
|
29
29
|
- business-panel
|
|
30
30
|
- index-repo
|
|
31
|
-
argument-hint: "<command|task> [args...] [--cli auto|codex|antigravity|claude] [--mode quick|deep|consensus] [--risk-tier auto|low|medium|high] [--shape consensus|debate|panel] [--cli-set triad|no-antigravity|custom] [--parallel 1|N|swarm] [--retry 0|1|ralph] [--isolation none|worktree] [--remote <host>|none]"
|
|
31
|
+
argument-hint: "<command|task> [args...] [--cli auto|codex|antigravity|claude] [--mode quick|deep|consensus] [--risk-tier auto|low|medium|high] [--shape consensus|debate|panel] [--cli-set triad|no-antigravity|custom] [--parallel 1|N|swarm] [--retry 0|1|ralph] [--isolation none|worktree] [--remote <host>|none] [--skill <name>]"
|
|
32
32
|
---
|
|
33
33
|
|
|
34
34
|
# tfx-auto — 통합 CLI 오케스트레이터
|
|
@@ -169,6 +169,7 @@ ARGUMENTS 에 아래 플래그가 있으면 Step 0 스마트 라우팅의 내부
|
|
|
169
169
|
| `--no-claude-native` | false (기본) | Claude native sub-agent 경로 유지 | — |
|
|
170
170
|
| `--no-claude-native` | true | Claude native 경로 disable, CLI 기반 worker 강제 | tfx-route.sh |
|
|
171
171
|
| `--max-iterations` | `0` (기본, unlimited) | `--retry ralph`/`auto-escalate` 상한 | retry-state-machine.mjs |
|
|
172
|
+
| `--skill` | `<name>` | `skills/<name>/SKILL.md` 본문을 codex/agy 프롬프트 앞에 주입 (`TFX_INJECT_SKILL`). 미지정 시 no-op | tfx-route.sh |
|
|
172
173
|
|
|
173
174
|
### `--risk-tier` 계약
|
|
174
175
|
|
|
@@ -204,6 +205,37 @@ ARGUMENTS 에 아래 플래그가 있으면 Step 0 스마트 라우팅의 내부
|
|
|
204
205
|
- `--retry ralph` → true ralph state machine (Phase 3, retry-state-machine.mjs)
|
|
205
206
|
- `--retry auto-escalate` → CLI 승격 체인 (Phase 3)
|
|
206
207
|
- `--max-iterations N` (N>0) → ralph/auto-escalate 에 상한 부여
|
|
208
|
+
- `--skill <name>` → `TFX_INJECT_SKILL=<name>` 로 tfx-route.sh 에 전달. 스킬 파일 부재 시 warning 후 주입 생략 (fail-open, 작업은 계속)
|
|
209
|
+
|
|
210
|
+
### 스킬 주입 (`--skill <name>`)
|
|
211
|
+
|
|
212
|
+
codex/agy 워커 프롬프트 앞에 등록된 스킬의 방법론을 주입하는 **opt-in** 프레임워크. 설계 근거는 `decision-skill-passing` (omc 레퍼런스 + codex/agy 네이티브 스킬 조사):
|
|
213
|
+
|
|
214
|
+
- **메커니즘**: `--skill <name>` → tfx-route.sh `TFX_INJECT_SKILL=<name>`. `skills/<name>/SKILL.md` 본문을 `--- SKILL: <name> (apply this methodology...) ---` delimited block 으로 프롬프트 앞에 prepend. codex·agy 레인 공통 (CLI-agnostic, `prepend_skill`).
|
|
215
|
+
- **기본 off**: 미지정이면 no-op → 현행 동작 그대로. 회귀 위험 없음.
|
|
216
|
+
- **파싱 안전**: prepend 는 `printf`/`cat` → temp file 만 사용. codex 는 argv `--` 뒤, agy 는 stdin `printf %s` 로 전달되므로 `$`(codex)·`₩`/백슬래시(agy) 같은 특수문자를 셸 재확장 없이 **리터럴 보존**. 스킬 본문을 그대로 넘겨도 깨지지 않음.
|
|
217
|
+
- **네이티브 스킬을 안 쓰는 이유**: codex `$skill-name`/`/name` 슬래시와 agy `/name` 은 둘 다 **인터랙티브 TUI 전용** → headless one-shot 에서 named-skill 강제 호출 불가 (미문서). 그래서 prose 주입을 채택 (레퍼런스 omc 도 role .md 를 raw prepend, 네이티브 회피).
|
|
218
|
+
|
|
219
|
+
#### 커맨드→스킬 매핑 (tfx-auto convention, opt-in)
|
|
220
|
+
|
|
221
|
+
tfx-auto 가 커맨드/agent 별로 주입할 스킬을 고를 때 쓰는 **권고 테이블**. 기본은 매핑 없음 (명시 `--skill` 만 작동) — 부적절한 스킬이 모든 프롬프트에 새는 것을 막는다. 프로젝트가 명시적으로 활성화할 때만 아래를 적용해 `--skill` 을 자동 부여한다.
|
|
222
|
+
|
|
223
|
+
| 커맨드/agent | 권고 스킬 | 비고 |
|
|
224
|
+
|--------------|-----------|------|
|
|
225
|
+
| (기본) | 없음 | explicit `--skill` 우선 |
|
|
226
|
+
| 프로젝트 정의 | 프로젝트가 지정 | 활성화 시 tfx-auto 가 해당 `--skill` set |
|
|
227
|
+
|
|
228
|
+
> 매핑된 스킬 파일이 없으면 `prepend_skill` 이 warning 후 주입을 생략한다 (fail-open). 존재하지 않는 매핑을 "주입됨"으로 가정하지 않는다.
|
|
229
|
+
|
|
230
|
+
#### agy anti-overclaim (자동, agy 레인 전용)
|
|
231
|
+
|
|
232
|
+
agy(Gemini 3.x) 레인은 `TFX_AGY_ANTI_OVERCLAIM`(기본 on) 으로 완료/grounding 규율 블록을 프롬프트 **END** 에 자동 append (`append_agy_anti_overclaim`). Gemini 3.x 과신(AA-Omniscience 실측: 정확도 53-56% 대비 환각률 88-91%) 대응:
|
|
233
|
+
|
|
234
|
+
- fresh 증거 없이 done/fixed/passing 주장 금지.
|
|
235
|
+
- 검증 불가 시 `No Info / 확인 불가` 후 중단 (날조 금지).
|
|
236
|
+
- 추론은 주어진 context 로, confident guessing 보다 accurate abstention 우선.
|
|
237
|
+
|
|
238
|
+
부정 제약은 Gemini 3 공식 가이드대로 **END 배치**하고 blanket "do not guess" 는 역효과라 피한다. opt-out: `TFX_AGY_ANTI_OVERCLAIM=0`.
|
|
207
239
|
|
|
208
240
|
### Retry state machine 계약 (legacy autoroute / persist 이관)
|
|
209
241
|
|