create-byan-agent 2.60.1 → 2.60.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.
- package/CHANGELOG.md +17 -0
- package/README.md +10 -0
- package/install/bin/byan-communication.cjs +11 -0
- package/install/lib/communication-manifest.json +243 -0
- package/install/lib/communication-migration.cjs +91 -0
- package/install/lib/communication-previous.json +181 -0
- package/install/lib/gitignore.js +135 -0
- package/install/lib/install-engine.js +17 -1
- package/install/package.json +1 -1
- package/install/templates/.claude/agents/bmad-byan.md +13 -26
- package/install/templates/.claude/agents/bmad-tao.md +47 -0
- package/install/templates/.claude/hooks/auto-dispatch-gate.js +81 -0
- package/install/templates/.claude/hooks/inject-tao.js +15 -71
- package/install/templates/.claude/hooks/inject-voice-anchor.js +21 -154
- package/install/templates/.claude/hooks/lib/dispatch-approval.cjs +135 -0
- package/install/templates/.claude/hooks/lib/dispatch-plan-format.js +293 -0
- package/install/templates/.claude/hooks/lib/plain-language.js +7 -14
- package/install/templates/.claude/hooks/lib/rtk-coverage.js +31 -0
- package/install/templates/.claude/hooks/lib/tool-log.js +143 -0
- package/install/templates/.claude/hooks/lib/voice-conformance.js +9 -32
- package/install/templates/.claude/hooks/lib/webfetch-output.js +37 -0
- package/install/templates/.claude/hooks/rtk-coverage.js +18 -0
- package/install/templates/.claude/hooks/tool-failure-guard.js +16 -9
- package/install/templates/.claude/hooks/webfetch-output.js +17 -0
- package/install/templates/.claude/rules/native-workflows.md +24 -5
- package/install/templates/.claude/rules/plain-language.md +7 -86
- package/install/templates/.claude/settings.json +56 -0
- package/install/templates/.claude/skills/byan-byan/SKILL.md +42 -83
- package/install/templates/.claude/skills/byan-hermes-dispatch/SKILL.md +22 -7
- package/install/templates/.claude/workflows/byan-auto-dispatch.js +86 -38
- package/install/templates/.codex/skills/byan/SKILL.md +7 -0
- package/install/templates/AGENTS.md +106 -0
- package/install/templates/_byan/_config/communication-policy.json +50 -0
- package/install/templates/_byan/_config/workflow-manifest.csv +2 -0
- package/install/templates/_byan/agent/byan/byan-soul.md +14 -2
- package/install/templates/_byan/agent/byan/byan-tao.md +35 -274
- package/install/templates/_byan/agent/byan/byan.md +6 -2
- package/install/templates/_byan/agent/byan/soul.md +419 -0
- package/install/templates/_byan/agent/byan/tao.md +60 -0
- package/install/templates/_byan/agent/tao/tao.md +26 -27
- package/install/templates/_byan/connaissance/mantras-sources.md +295 -0
- package/install/templates/_byan/core/activation/soul-activation.md +6 -5
- package/install/templates/_byan/core/communication.cjs +76 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/agent-matcher.js +38 -1
- package/install/templates/_byan/mcp/byan-mcp-server/lib/communication.cjs +77 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/dispatch-router.js +12 -3
- package/install/templates/_byan/mcp/byan-mcp-server/lib/dispatch.js +51 -20
- package/install/templates/_byan/mcp/byan-mcp-server/lib/fd-ack.js +75 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/leantime-fd-core.js +12 -1
- package/install/templates/_byan/mcp/byan-mcp-server/lib/native-tiers.js +114 -2
- package/install/templates/_byan/mcp/byan-mcp-server/lib/routing-ledger.js +356 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/soul.js +9 -0
- package/install/templates/_byan/mcp/byan-mcp-server/server.js +24 -6
- package/install/templates/_byan/mcp/byan-mcp-server/skill-bundles-manifest.json +2 -2
- package/install/templates/_byan/workflow/simple/byan/data/mantras.yaml +2 -5
- package/install/templates/_byan/workflow/simple/byan/templates/tao-template.md +56 -0
- package/install/templates/docs/auto-dispatch.md +76 -0
- package/install/templates/docs/communication.md +69 -0
- package/install/templates/docs/native-workflows-contract.md +33 -11
- package/install/templates/docs/tool-output-cost.md +88 -0
- package/install/templates/scripts/communication-eval.cjs +192 -0
- package/install/templates/scripts/rtk-coverage-report.js +30 -0
- package/install/templates/src/byan-v2/data/mantras.json +1058 -0
- package/install/templates/tests/communication/README.md +106 -0
- package/install/templates/tests/communication/incises.test.cjs +26 -0
- package/install/templates/tests/communication/scenarios.json +391 -0
- package/package.json +1 -1
- package/src/byan-v2/data/mantras.json +26 -6
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
classifyLeaf,
|
|
3
|
+
tierFor,
|
|
4
|
+
TIER_MODEL,
|
|
5
|
+
LEAF_TYPES,
|
|
6
|
+
modelForTask,
|
|
7
|
+
effortForTask,
|
|
8
|
+
tierOfModel,
|
|
9
|
+
COMPLEXITY_SOURCES,
|
|
10
|
+
PROTECTED_FLOOR_COMPLEXITY,
|
|
11
|
+
} from './native-tiers.js';
|
|
2
12
|
|
|
3
13
|
// byan_dispatch routes a unit of work along TWO independent axes:
|
|
4
14
|
//
|
|
@@ -6,16 +16,22 @@ import { classifyLeaf, tierFor, TIER_MODEL, LEAF_TYPES } from './native-tiers.js
|
|
|
6
16
|
// Derived from the scalar score + parallelizable. This is
|
|
7
17
|
// dispatch's own concern: orchestration.
|
|
8
18
|
// TIER — WHICH model the work deserves. Delegated to native-tiers (the
|
|
9
|
-
// single source of truth),
|
|
10
|
-
//
|
|
11
|
-
// verification, analysis (and anything unmatched) stay deep =
|
|
12
|
-
// inherit the session model. We never PIN UP to opus.
|
|
19
|
+
// single source of truth), au croisement de la NATURE et de la
|
|
20
|
+
// COMPLEXITE DECLAREE de la tache.
|
|
13
21
|
//
|
|
14
22
|
// Before this split the two axes were fused into one route string
|
|
15
23
|
// ('mcp-worker-haiku', 'main-thread-opus'), so a short sequential task was
|
|
16
24
|
// silently downgraded to haiku purely on length, and a long one was pinned up to
|
|
17
25
|
// opus — exactly the size-driven mis-tiering native-tiers' anti-downgrade doctrine
|
|
18
|
-
// forbids. The score still picks the strategy; the model
|
|
26
|
+
// forbids. The score still picks the strategy; the model comes from the crossing.
|
|
27
|
+
//
|
|
28
|
+
// La nature seule ne suffisait pas non plus : elle rendait le meme verdict pour
|
|
29
|
+
// un hello world et pour un moteur de resolution de dependances, tous deux de
|
|
30
|
+
// l'implementation, donc tous deux sur le modele de session — le plus cher. La
|
|
31
|
+
// complexite tranche desormais entre les deux, mais SEULEMENT quand un appelant
|
|
32
|
+
// l'a declaree. Sans elle il ne reste que la longueur du prompt, qui mesure des
|
|
33
|
+
// caracteres, pas de la difficulte : la faire decider du modele reintroduirait
|
|
34
|
+
// exactement la retrogradation par la taille condamnee ci-dessus.
|
|
19
35
|
//
|
|
20
36
|
// The dependency on native-tiers is intentional and one-directional: dispatch
|
|
21
37
|
// CONSUMES the tier source of truth, it does not duplicate it. native-tiers is a
|
|
@@ -25,10 +41,9 @@ import { classifyLeaf, tierFor, TIER_MODEL, LEAF_TYPES } from './native-tiers.js
|
|
|
25
41
|
const VALID_NATURES = new Set(Object.values(LEAF_TYPES));
|
|
26
42
|
|
|
27
43
|
export function dispatch({ task, complexity, parallelizable, nature } = {}) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
: Math.min(100, Math.floor((task?.length || 0) / 10));
|
|
44
|
+
const declaree = typeof complexity === 'number' && Number.isFinite(complexity);
|
|
45
|
+
const score = declaree ? complexity : Math.min(100, Math.floor((task?.length || 0) / 10));
|
|
46
|
+
const complexitySource = declaree ? COMPLEXITY_SOURCES.DECLARED : COMPLEXITY_SOURCES.ESTIMATED;
|
|
32
47
|
const isPar = parallelizable === true;
|
|
33
48
|
|
|
34
49
|
// Axis 1 — strategy (where). Scalar, as before, minus the fused model suffix.
|
|
@@ -47,28 +62,38 @@ export function dispatch({ task, complexity, parallelizable, nature } = {}) {
|
|
|
47
62
|
strategyReason = `score ${score} >= 40: heavy, kept in the main thread`;
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
// Axis 2 — tier (which model).
|
|
51
|
-
// nature wins; otherwise classify the task text. An unknown
|
|
52
|
-
// classification rather than guessing, and classification's
|
|
53
|
-
// IMPLEMENTATION (
|
|
54
|
-
// work is never downgraded on a miss.
|
|
65
|
+
// Axis 2 — tier (which model). Croisement nature x complexite, via native-tiers.
|
|
66
|
+
// An explicit, valid nature wins; otherwise classify the task text. An unknown
|
|
67
|
+
// nature falls back to classification rather than guessing, and classification's
|
|
68
|
+
// own default is IMPLEMENTATION (protege), so the conservative path is the worst
|
|
69
|
+
// case — protected work is never downgraded on a miss.
|
|
55
70
|
const leafType = VALID_NATURES.has(nature) ? nature : classifyLeaf({ label: task || '' });
|
|
56
|
-
const
|
|
57
|
-
|
|
71
|
+
const model = modelForTask({ nature: leafType, complexity: score, source: complexitySource });
|
|
72
|
+
// Le niveau annonce decrit le modele REELLEMENT choisi, pas la classe theorique
|
|
73
|
+
// de la nature : sinon un verdict se contredit (tier deep + modele haiku).
|
|
74
|
+
const tier = tierOfModel(model);
|
|
58
75
|
|
|
59
76
|
const tierReason =
|
|
60
|
-
|
|
61
|
-
? `nature=${leafType} -> ${tier}:
|
|
62
|
-
: `nature=${leafType} -> ${tier}: ${model}
|
|
77
|
+
complexitySource === COMPLEXITY_SOURCES.ESTIMATED
|
|
78
|
+
? `nature=${leafType}, complexite non declaree (score ${score} devine sur la longueur du prompt) -> ${tier}: ${model === null ? 'modele de session' : model}. Passe \`complexity\` (0-100) pour activer l'echelle par complexite`
|
|
79
|
+
: `nature=${leafType} + complexite declaree ${score} -> ${tier}: ${model === null ? 'modele de session' : model}${
|
|
80
|
+
leafType === LEAF_TYPES.VERIFICATION
|
|
81
|
+
? ' (ligne rouge: une verification ne descend jamais)'
|
|
82
|
+
: score >= PROTECTED_FLOOR_COMPLEXITY
|
|
83
|
+
? ' (au-dessus du plancher anti-retrogradation)'
|
|
84
|
+
: ''
|
|
85
|
+
}`;
|
|
63
86
|
|
|
64
87
|
// model applies to a DELEGATED strategy (subagent / mcp-worker leaf); for a
|
|
65
88
|
// main-thread strategy the work runs on the session model and model is advisory.
|
|
66
89
|
return {
|
|
67
90
|
score,
|
|
91
|
+
complexitySource,
|
|
68
92
|
strategy,
|
|
69
93
|
nature: leafType,
|
|
70
94
|
tier,
|
|
71
95
|
model,
|
|
96
|
+
effort: effortForTask({ nature: leafType, complexity: score, source: complexitySource }),
|
|
72
97
|
parallelizable: isPar,
|
|
73
98
|
reasoning: `${strategyReason}. ${tierReason}.`,
|
|
74
99
|
};
|
|
@@ -81,6 +106,12 @@ export function dispatch({ task, complexity, parallelizable, nature } = {}) {
|
|
|
81
106
|
// script's concern, only WHICH model each deserves is answered. An explicit
|
|
82
107
|
// valid nature wins; otherwise the label classifies; a miss stays protected
|
|
83
108
|
// (implementation -> deep -> null), same conservative path as dispatch().
|
|
109
|
+
//
|
|
110
|
+
// Pas de croisement avec la complexite ici, volontairement : une feuille de
|
|
111
|
+
// script n'en porte pas (le linter et le garde-fou d'ecriture ne voient qu'un
|
|
112
|
+
// libelle), et inventer un score par feuille rouvrirait la retrogradation par la
|
|
113
|
+
// taille. Le croisement appartient au dispatch de TACHE, ou un appelant peut
|
|
114
|
+
// declarer la complexite.
|
|
84
115
|
export function dispatchBatch(leaves) {
|
|
85
116
|
if (!Array.isArray(leaves)) return [];
|
|
86
117
|
return leaves.map((leaf) => {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L'accuse de reception des outils d'ECRITURE du cycle FD.
|
|
3
|
+
*
|
|
4
|
+
* POURQUOI, ET C'EST MESURE. Le 2026-09-01, sur une seule session :
|
|
5
|
+
* byan_fd_update et byan_fd_advance ont rendu 43 130 jetons pour 17 appels,
|
|
6
|
+
* soit 38 % de tout ce qui est revenu d'un outil ce jour-la. Chaque ecriture
|
|
7
|
+
* renvoyait l'etat ENTIER — backlog redige, idees brutes, table de dispatch,
|
|
8
|
+
* notes, historique des phases — et en plus mis en forme sur deux espaces.
|
|
9
|
+
*
|
|
10
|
+
* Or celui qui ecrit vient de composer cet etat : le lui relire en entier ne
|
|
11
|
+
* lui apprend rien. Ce qu'il a besoin de savoir tient en trois lignes : ou en
|
|
12
|
+
* est le cycle, combien d'items restent, et ce qui vient de changer.
|
|
13
|
+
*
|
|
14
|
+
* LE FICHIER RESTE LA SOURCE DE VERITE. _byan-output/fd-state.json est ecrit
|
|
15
|
+
* avant que l'accuse parte ; qui veut l'etat complet appelle byan_fd_status,
|
|
16
|
+
* dont c'est precisement le metier. On ne perd donc aucune information — on
|
|
17
|
+
* cesse seulement de la repeter a quelqu'un qui vient de la donner.
|
|
18
|
+
*
|
|
19
|
+
* CE QUE L'ACCUSE NE DOIT SURTOUT PAS ETRE. Un etat d'apparence complete.
|
|
20
|
+
* leantime-fd-sync.js lit l'etat depuis le retour de l'outil, avec un repli
|
|
21
|
+
* sur le fichier ; sa garde parseFdState exige donc un backlog, pas seulement
|
|
22
|
+
* une phase. Un accuse est reconnu comme tel et declenche le repli.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const STATUTS = ['pending', 'building', 'done', 'skipped'];
|
|
26
|
+
|
|
27
|
+
function compterBacklog(backlog) {
|
|
28
|
+
const out = { total: 0 };
|
|
29
|
+
for (const s of STATUTS) out[s] = 0;
|
|
30
|
+
if (!Array.isArray(backlog)) return out;
|
|
31
|
+
out.total = backlog.length;
|
|
32
|
+
for (const item of backlog) {
|
|
33
|
+
const s = item && typeof item.status === 'string' ? item.status : null;
|
|
34
|
+
if (s && Object.prototype.hasOwnProperty.call(out, s)) out[s] += 1;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Construit l'accuse a partir de l'etat qui vient d'etre ecrit.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} state l'etat FD complet, tel qu'il vient d'etre persiste
|
|
43
|
+
* @param {object} [extra] { changed?: string[], from?: string, note?: string }
|
|
44
|
+
* @returns {object} un accuse compact, jamais une exception
|
|
45
|
+
*/
|
|
46
|
+
export function fdAck(state, extra = {}) {
|
|
47
|
+
const s = state && typeof state === 'object' && !Array.isArray(state) ? state : {};
|
|
48
|
+
const e = extra && typeof extra === 'object' ? extra : {};
|
|
49
|
+
|
|
50
|
+
const ack = {
|
|
51
|
+
ok: true,
|
|
52
|
+
fd_id: s.fd_id ?? null,
|
|
53
|
+
feature_name: s.feature_name ?? null,
|
|
54
|
+
phase: typeof s.phase === 'string' ? s.phase : null,
|
|
55
|
+
backlog: compterBacklog(s.backlog),
|
|
56
|
+
// Des COMPTES, pas le contenu : c'est le contenu qui pesait.
|
|
57
|
+
counts: {
|
|
58
|
+
raw_ideas: Array.isArray(s.raw_ideas) ? s.raw_ideas.length : 0,
|
|
59
|
+
notes: Array.isArray(s.notes) ? s.notes.length : 0,
|
|
60
|
+
dispatch_table: Array.isArray(s.dispatch_table) ? s.dispatch_table.length : 0,
|
|
61
|
+
commits: Array.isArray(s.commits) ? s.commits.length : 0,
|
|
62
|
+
},
|
|
63
|
+
// Ou lire l'etat complet quand on en a vraiment besoin. Le dire dans
|
|
64
|
+
// l'accuse evite d'avoir a s'en souvenir.
|
|
65
|
+
state_file: '_byan-output/fd-state.json',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (Array.isArray(e.changed) && e.changed.length) ack.changed = e.changed;
|
|
69
|
+
if (typeof e.from === 'string' && e.from) ack.from = e.from;
|
|
70
|
+
if (typeof e.note === 'string' && e.note) ack.note = e.note;
|
|
71
|
+
|
|
72
|
+
return ack;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default { fdAck };
|
|
@@ -47,7 +47,18 @@ export function parseFdState(toolResponse) {
|
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
// UNE PHASE NE SUFFIT PAS A FAIRE UN ETAT. Depuis que les outils d'ecriture
|
|
51
|
+
// FD rendent un accuse compact au lieu de l'etat entier (lib/fd-ack.js, ne
|
|
52
|
+
// du constat que ces retours pesaient 38 % de tout ce qui revenait d'un
|
|
53
|
+
// outil), un objet peut porter une `phase` sans etre un etat. L'accepter
|
|
54
|
+
// ferait travailler decideActions sur un backlog absent, au lieu de retomber
|
|
55
|
+
// sur le fichier, qui est la source de verite. On exige donc le backlog.
|
|
56
|
+
if (
|
|
57
|
+
candidate &&
|
|
58
|
+
typeof candidate === 'object' &&
|
|
59
|
+
typeof candidate.phase === 'string' &&
|
|
60
|
+
Array.isArray(candidate.backlog)
|
|
61
|
+
) {
|
|
51
62
|
return candidate;
|
|
52
63
|
}
|
|
53
64
|
return null;
|
|
@@ -91,8 +91,16 @@ export const DEEP_PREFIX = 'deep-';
|
|
|
91
91
|
// check/review/gate/audit/assert/lint; a leaf that runs tests is labelled
|
|
92
92
|
// 'verify-*' in practice.
|
|
93
93
|
const VERIFICATION_KEYWORDS = ['verify', 'validate', 'check', 'assert', 'gate', 'lint', 'audit', 'review'];
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// 'concev'/'concept' : le jumeau francais de 'design'. Sans lui, "Concevoir un
|
|
95
|
+
// moteur ... avec detection de cycles" tombait sur 'detect' et repartait en
|
|
96
|
+
// exploration, donc sur le bas de gamme — une tache de conception retrogradee
|
|
97
|
+
// par un mot du milieu de phrase.
|
|
98
|
+
const ANALYSIS_KEYWORDS = ['analy', 'design', 'architect', 'assess', 'evaluate', 'strategy', 'risk', 'nfr', 'recommend', 'judge', 'score', 'coverage', 'synthes', 'concev', 'concept'];
|
|
99
|
+
// Meme raison cote fabrication : "corriger la liste des dependances" contient
|
|
100
|
+
// 'list' et repartait en exploration. La priorite protege-d-abord ne joue que
|
|
101
|
+
// si le vocabulaire protege existe aussi en francais. Les radicaux couvrent
|
|
102
|
+
// les conjugaisons courantes (corrige, correction, redaction...).
|
|
103
|
+
const IMPLEMENTATION_KEYWORDS = ['implement', 'build', 'write', 'generate', 'create', 'dev', 'rgr', 'refactor', 'fix', 'scaffold', 'save', 'optimize', 'aggregate', 'report', 'present', 'plan', 'map', 'select', 'subprocess', 'sub-', 'ecrire', 'redig', 'corrig', 'construi', 'developp', 'ajout', 'modifi'];
|
|
96
104
|
// cartograph/inventaire/recensement... : vocabulaire de cartographie de
|
|
97
105
|
// l'existant (lecture + synthese), francais inclus — cas terrain byan_web ou
|
|
98
106
|
// des feuilles de scan DISCOVERY non reconnues heritaient d'une session Fable 5.
|
|
@@ -171,3 +179,107 @@ export function isKnownTierModel(modelId) {
|
|
|
171
179
|
export function isDowngradeModel(modelId) {
|
|
172
180
|
return modelId === TIER_MODEL.cheap || modelId === TIER_MODEL.balanced;
|
|
173
181
|
}
|
|
182
|
+
|
|
183
|
+
// =========================================================================
|
|
184
|
+
// Croisement NATURE x COMPLEXITE (surface "dispatch de tache")
|
|
185
|
+
// =========================================================================
|
|
186
|
+
// Tout ce qui precede repond a "quel modele merite cette FEUILLE de workflow",
|
|
187
|
+
// une question sans complexite : le linter lit un script statique, il n'a qu'un
|
|
188
|
+
// libelle. Ce qui suit repond a une question differente : "quel modele merite
|
|
189
|
+
// cette TACHE", quand un score de complexite existe. Meme taxonomie de natures,
|
|
190
|
+
// deux surfaces. La nature seule ne peut pas trancher : elle dit combien de
|
|
191
|
+
// jugement le travail porte, pas s'il est dur. Un hello world et un moteur de
|
|
192
|
+
// resolution de dependances sont tous deux de l'implementation ; les router sur
|
|
193
|
+
// le meme modele est le defaut que ce bloc corrige.
|
|
194
|
+
//
|
|
195
|
+
// L'echelle par complexite n'est PAS redefinie ici : elle vit dans
|
|
196
|
+
// dispatch-router.js (claudeModelForComplexity) et ce module la consomme. Deux
|
|
197
|
+
// echelles finiraient par diverger — exactement le defaut qu'on corrige.
|
|
198
|
+
export { claudeModelForComplexity as modelForComplexity } from './dispatch-router.js';
|
|
199
|
+
import { claudeModelForComplexity, claudeEffortForModel } from './dispatch-router.js';
|
|
200
|
+
|
|
201
|
+
// Provenance du score de complexite. DECLARED = un appelant l'a juge et fourni.
|
|
202
|
+
// ESTIMATED = personne ne l'a juge, il a ete devine (longueur du prompt chez
|
|
203
|
+
// dispatch()). La distinction porte une decision : la longueur d'un prompt dit
|
|
204
|
+
// long ou court, jamais facile ou difficile ("refactor auth" tient en treize
|
|
205
|
+
// caracteres), donc un score estime n'autorise ni retrogradation ni montee en
|
|
206
|
+
// gamme d'un travail protege.
|
|
207
|
+
export const COMPLEXITY_SOURCES = Object.freeze({ DECLARED: 'declared', ESTIMATED: 'estimated' });
|
|
208
|
+
|
|
209
|
+
// Seuil du plancher anti-retrogradation (B2). Au-dessus, une implementation est
|
|
210
|
+
// tenue pour complexe : son modele ne peut plus etre un modele de repli. En
|
|
211
|
+
// dessous, l'echelle a le droit de descendre — une tache simple n'a pas besoin
|
|
212
|
+
// du modele le plus cher, et c'est tout l'objet du chantier.
|
|
213
|
+
// 67 est precisement le barreau ou l'echelle elle-meme cesse de descendre
|
|
214
|
+
// (opus) : les deux regles se rejoignent au lieu de se contredire, et le
|
|
215
|
+
// plancher reste un garde-fou si l'echelle bougeait un jour.
|
|
216
|
+
export const PROTECTED_FLOOR_COMPLEXITY = 67;
|
|
217
|
+
|
|
218
|
+
// applyProtectedFloor(model, complexity) -> le modele apres plancher.
|
|
219
|
+
// Le plancher ne fait qu'une chose : au-dessus du seuil, refuser un modele de
|
|
220
|
+
// repli et rendre null (herite du modele de session). Il ne descend jamais rien
|
|
221
|
+
// et ne bloque pas une montee en gamme.
|
|
222
|
+
export function applyProtectedFloor(model, complexity) {
|
|
223
|
+
if (!(complexity >= PROTECTED_FLOOR_COMPLEXITY)) return model;
|
|
224
|
+
return isDowngradeModel(model) ? null : model;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// tierOfModel(model) -> le niveau annonce pour un modele effectivement choisi.
|
|
228
|
+
// Necessaire depuis que le modele ne sort plus de la seule nature : le champ
|
|
229
|
+
// "tier" doit decrire ce qui a ete decide, pas la classe theorique de la nature,
|
|
230
|
+
// sinon un verdict se contredit lui-meme (tier deep + modele haiku). Les modeles
|
|
231
|
+
// de haut de gamme retombent sur deep : deep veut dire "rien n'a ete retrograde".
|
|
232
|
+
export function tierOfModel(model) {
|
|
233
|
+
if (model === TIER_MODEL.cheap) return TIERS.CHEAP;
|
|
234
|
+
if (model === TIER_MODEL.balanced) return TIERS.BALANCED;
|
|
235
|
+
return TIERS.DEEP;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function clampComplexity(complexity) {
|
|
239
|
+
return Math.min(100, Math.max(0, complexity));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// modelForTask({ nature, complexity, source }) -> le modele a passer (chaine) ou
|
|
243
|
+
// null (herite du modele de session). C'est le croisement des deux axes.
|
|
244
|
+
//
|
|
245
|
+
// La nature decide COMMENT la complexite a le droit de jouer :
|
|
246
|
+
// verification : elle ne joue pas. Ligne rouge — un controleur qu'on
|
|
247
|
+
// retrograde ne controle plus rien, et on ne le pin pas non
|
|
248
|
+
// plus en gamme : il reste sur le modele de session, comme
|
|
249
|
+
// dans le script byan-auto-dispatch.
|
|
250
|
+
// mechanique : elle ne joue pas non plus. La nature est declaree sans
|
|
251
|
+
// jugement (un JSON parse ou ne parse pas) ; sa difficulte
|
|
252
|
+
// annoncee ne change pas ce fait.
|
|
253
|
+
// exploration : l'echelle joue, plafonnee a sonnet. Un scan enorme merite
|
|
254
|
+
// mieux que le bas de gamme, jamais le haut de gamme.
|
|
255
|
+
// analyse : l'echelle joue, plancher a sonnet (le linter refuse haiku
|
|
256
|
+
// sur une analyse) et montee en gamme permise.
|
|
257
|
+
// implementation: l'echelle joue en entier, sous le plancher conditionnel.
|
|
258
|
+
export function modelForTask({ nature, complexity, source = COMPLEXITY_SOURCES.ESTIMATED } = {}) {
|
|
259
|
+
if (nature === LEAF_TYPES.VERIFICATION) return null;
|
|
260
|
+
if (nature === LEAF_TYPES.MECHANICAL) return TIER_MODEL[TIERS.BALANCED];
|
|
261
|
+
|
|
262
|
+
const tierDeLaNature = tierFor(nature);
|
|
263
|
+
// Sans score exploitable, ou avec un score seulement devine, on s'en tient a
|
|
264
|
+
// la nature : c'est le comportement d'avant, et il est protecteur.
|
|
265
|
+
if (!Number.isFinite(complexity) || source !== COMPLEXITY_SOURCES.DECLARED) {
|
|
266
|
+
return TIER_MODEL[tierDeLaNature];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const cx = clampComplexity(complexity);
|
|
270
|
+
const echelon = claudeModelForComplexity(cx);
|
|
271
|
+
|
|
272
|
+
if (nature === LEAF_TYPES.EXPLORATION) {
|
|
273
|
+
return isUpTierModel(echelon) ? TIER_MODEL[TIERS.BALANCED] : echelon;
|
|
274
|
+
}
|
|
275
|
+
if (nature === LEAF_TYPES.ANALYSIS) {
|
|
276
|
+
return echelon === TIER_MODEL.cheap ? TIER_MODEL[TIERS.BALANCED] : echelon;
|
|
277
|
+
}
|
|
278
|
+
return applyProtectedFloor(echelon, cx);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// The effective model includes nature floors/caps and the verification guard.
|
|
282
|
+
// Deriving effort afterwards keeps both knobs on the same rung.
|
|
283
|
+
export function effortForTask(task) {
|
|
284
|
+
return claudeEffortForModel(modelForTask(task));
|
|
285
|
+
}
|