harness-everything 0.30.4 → 0.30.6

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 (71) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +38 -0
  4. package/README.md +17 -7
  5. package/bin/cli.js +5 -0
  6. package/ci/fable-mode-test.js +112 -51
  7. package/ci/mechanism-2t-workspace-identity.test.js +3 -1
  8. package/ci/mechanism-2w-platform-compatibility.test.js +39 -0
  9. package/ci/mechanism-2z-claude-plugin-manifest.test.js +12 -0
  10. package/ci/router-workflow-plan.test.js +92 -7
  11. package/docs/platform-capabilities.md +14 -1
  12. package/docs/troubleshooting.md +31 -1
  13. package/docs/workflows/fable-mode.md +10 -9
  14. package/evals/fable-mode/tasks/negative-tier2-no-model.yaml +3 -3
  15. package/evals/fable-mode/tasks/positive-haiku-mode.yaml +1 -1
  16. package/evals/fable-mode/tasks/positive-opus-mode.yaml +1 -1
  17. package/evals/fable-mode/tasks/positive-sonnet-mode.yaml +1 -1
  18. package/evals/fable-mode/tasks/positive-trigger.yaml +1 -1
  19. package/fable-mode/SKILL.md +18 -12
  20. package/fable-mode/agents/fable-orchestrator.md +7 -6
  21. package/fable-mode/agents/fable-verifier.md +1 -1
  22. package/fable-mode/agents/fable-worker-haiku.md +5 -4
  23. package/fable-mode/agents/fable-worker-sonnet.md +5 -4
  24. package/fable-mode/behavior-profile-matrix.json +36 -0
  25. package/fable-mode/execution-guardrails/SKILL.md +3 -6
  26. package/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
  27. package/fable-mode/fable-haiku/SKILL.md +7 -5
  28. package/fable-mode/fable-haiku/references/routing-notes.md +2 -1
  29. package/fable-mode/fable-opus/SKILL.md +8 -6
  30. package/fable-mode/fable-sonnet/SKILL.md +8 -6
  31. package/fable-mode/model-matrix.json +7 -30
  32. package/fable-mode/references/model-matrix.md +35 -30
  33. package/fable-mode/references/profile-lookup.md +19 -0
  34. package/fable-mode/runtime-model-floor-matrix.json +47 -0
  35. package/fable-mode/scripts/model-selector.js +106 -60
  36. package/fable-mode/scripts/workflow-plan-consumer.js +1 -1
  37. package/harness-everything/SKILL.md +11 -14
  38. package/harness-everything/references/router-workflow-plan.md +2 -2
  39. package/harness-everything/schemas/router-task-shape.schema.json +3 -2
  40. package/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
  41. package/harness-everything/scripts/router-contract-core.js +19 -12
  42. package/harness-everything/scripts/tier-router.js +100 -12
  43. package/opencode-plugin/plugin.json +1 -1
  44. package/package.json +1 -1
  45. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  46. package/plugins/harness-everything/plugin.json +1 -1
  47. package/plugins/harness-everything/skills/fable-mode/SKILL.md +18 -12
  48. package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +7 -6
  49. package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +1 -1
  50. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +5 -4
  51. package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +5 -4
  52. package/plugins/harness-everything/skills/fable-mode/behavior-profile-matrix.json +36 -0
  53. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +3 -6
  54. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
  55. package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +7 -5
  56. package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +2 -1
  57. package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +8 -6
  58. package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +8 -6
  59. package/plugins/harness-everything/skills/fable-mode/model-matrix.json +7 -30
  60. package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +35 -30
  61. package/plugins/harness-everything/skills/fable-mode/references/profile-lookup.md +19 -0
  62. package/plugins/harness-everything/skills/fable-mode/runtime-model-floor-matrix.json +47 -0
  63. package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +106 -60
  64. package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +1 -1
  65. package/plugins/harness-everything/skills/harness-everything/SKILL.md +11 -14
  66. package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +2 -2
  67. package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +3 -2
  68. package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
  69. package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +19 -12
  70. package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +100 -12
  71. package/sbom.cdx.json +7 -7
@@ -46,11 +46,88 @@ function matchKeyword(prompt, keyword) {
46
46
  return new RegExp(`\\b${escaped}\\b`, 'i').test(prompt);
47
47
  }
48
48
 
49
+ const FABLE_PROFILE_INVOCATION = /\bfable(?:[- ]mode)?\s+(?:on|with)\s+(haiku|sonnet|sonnect|opus)\b|\bfable-(haiku|sonnet|opus)\b/i;
50
+ const PROFILE_LOOKUP_FIELDS = /\b(?:requestedProfile|effectiveProfile|profileAlias|assignedRole|runtimeModel|runtimeEffort)\b/gi;
51
+
49
52
  function detectFableModel(prompt) {
50
- const match = prompt.match(/\bfable(?:[- ]mode)?\s+(?:on|with)\s+(haiku|sonnet|sonnect|opus)\b|\bfable-(haiku|sonnet|opus)\b/i);
53
+ const match = prompt.match(FABLE_PROFILE_INVOCATION);
51
54
  if (!match) return null;
52
- const requested = (match[1] || match[2]).toLowerCase();
53
- return requested === 'sonnect' ? 'sonnet' : requested;
55
+ return (match[1] || match[2]).toLowerCase();
56
+ }
57
+
58
+ // A profile mention is data in a lookup, not an instruction to start Fable.
59
+ // Validate the whole clause, not just its prefix. Unknown prose conservatively
60
+ // retains execution routing; format field lists are recognized separately.
61
+ function containsOnlyLookupTerms(text, operation) {
62
+ const terms = {
63
+ explanation: /\b(?:please|help|me|explain|resolve|define|what|is|does|mean|means|meaning|the|a|an|of|to|into|in|for|behavior|behaviour|profile|profiles|selection|json|record|alias|role|runtime|model|effort|floor|fableprofile)\b/gi,
64
+ output: /\b(?:return|respond|output|show|provide|only|a|an|small|compact|json|record|selection|profile|result|with|fields|containing|include|including)\b/gi,
65
+ };
66
+ const chineseTerms = {
67
+ explanation: /(?:請|幫我|解釋|說明|解析|的意思|意思|含義|意義|是什麼|設定檔|行為|角色)/g,
68
+ output: /(?:只|僅|回傳|返回|輸出|顯示|提供|包含|含|的|欄位|記錄|紀錄|結果|設定檔)/g,
69
+ };
70
+ return text.replace(PROFILE_LOOKUP_FIELDS, '').replace(terms[operation], '')
71
+ .replace(chineseTerms[operation], '').replace(/[\s"'`“”‘’?::?]/g, '') === '';
72
+ }
73
+
74
+ // A negation scopes only its own noun/verb list. Vocabulary-only matching would
75
+ // let "no delegation, do execution" pass, so English negations follow a grammar
76
+ // and an un-negated continuation may only extend the noun list. Chinese nouns
77
+ // and verbs share forms, so Chinese lists must stay inside the negated clause.
78
+ const negatedList = unit => `${unit}(?:\\s+or\\s+${unit})*`;
79
+ const NEGATED_ITEM = '(?:orchestration|delegation|stages?|staging|execution|model[- ]switch(?:ing)?)';
80
+ const NEGATED_ACTION = '(?:switch(?:ing)?\\s+(?:the\\s+)?models?|delegate|orchestrate|stage|execute)';
81
+ const ENGLISH_NEGATION = new RegExp(`^(?:(?:no|without)\\s+${negatedList(NEGATED_ITEM)}|(?:do not|don't)\\s+${negatedList(NEGATED_ACTION)})$`, 'i');
82
+ const ENGLISH_NEGATION_CONTINUATION = new RegExp(`^(?:or\\s+)?${negatedList(NEGATED_ITEM)}$`, 'i');
83
+ const CHINESE_NEGATION_TERMS = /(?:不要|不需要|無需|不|或|編排|委派|階段|執行|切換模型)/g;
84
+
85
+ function isBoundedNegation(clause, continuation) {
86
+ const text = clause.replace(/’/g, "'").replace(/[“”"`?::?]/g, '').replace(/\s+/g, ' ').trim();
87
+ if (/^(?:不要|不需要|無需|不)/.test(text)) {
88
+ return !continuation && text.replace(CHINESE_NEGATION_TERMS, '').replace(/\s/g, '') === '';
89
+ }
90
+ return (continuation ? ENGLISH_NEGATION_CONTINUATION : ENGLISH_NEGATION).test(text);
91
+ }
92
+
93
+ function isFableProfileLookup(prompt, hasMacroSignal) {
94
+ if (!detectFableModel(prompt) || hasMacroSignal) return false;
95
+ if (/\b(?:direct[- ]single|iterative[- ]single|fable[- ](?:staged|parallel|multi[- ]agent[- ]workspace))\b/i.test(prompt)) return false;
96
+
97
+ const clauses = prompt
98
+ .split(/(?:[;,.,;。、]|\b(?:and|then|also|plus|afterwards|additionally)\b|(?:並且?|以及|接著|然後|另外))/i)
99
+ .map(part => part.trim())
100
+ .filter(Boolean);
101
+ if (clauses.length === 0) return false;
102
+
103
+ const [lookupClause, ...continuations] = clauses;
104
+ const quotedInvocation = new RegExp(`(["'\x60])(?:run|use|enter)\\s+(?:${FABLE_PROFILE_INVOCATION.source})\\1`, 'gi');
105
+ const normalizedLookup = lookupClause.replace(/[“”]/g, '"').replace(/[‘’]/g, "'")
106
+ .replace(quotedInvocation, 'fableprofile')
107
+ .replace(new RegExp(FABLE_PROFILE_INVOCATION.source, 'gi'), 'fableprofile');
108
+ const isLookupLead = /^(?:(?:please|help me)\s+|(?:請|幫我)\s*)?(?:explain|resolve|define|what (?:is|does)|解釋|說明|解析)/i.test(lookupClause)
109
+ && Boolean(detectFableModel(lookupClause));
110
+ if (!isLookupLead || !containsOnlyLookupTerms(normalizedLookup, 'explanation')) return false;
111
+
112
+ let continuationOperation = null;
113
+ for (const clause of continuations) {
114
+ const output = /^(?:please\s+)?(?:return|respond|output|show|provide)\b|^(?:請\s*)?(?:只|僅)?(?:回傳|返回|輸出|顯示|提供)/i.test(clause);
115
+ const negative = /^(?:please\s+)?(?:no|without|do not|don't)\b|^(?:請\s*)?(?:不要|不需要|無需|不)/i.test(clause);
116
+ if (output || negative) {
117
+ continuationOperation = output ? 'output' : 'negative';
118
+ const normalizedContinuation = clause.replace(/^(?:please\s+|請\s*)/i, '');
119
+ const bounded = output ? containsOnlyLookupTerms(normalizedContinuation, 'output')
120
+ : isBoundedNegation(normalizedContinuation, false);
121
+ if (!bounded) return false;
122
+ } else if (continuationOperation === 'output') {
123
+ // Only named canonical fields may continue a comma/and-separated list.
124
+ const remainder = clause.replace(PROFILE_LOOKUP_FIELDS, '').replace(/^(?:包含|欄位)/, '').replace(/[\s"'`“”‘’]/g, '');
125
+ if (remainder || !new RegExp(PROFILE_LOOKUP_FIELDS.source, 'i').test(clause)) return false;
126
+ } else if (continuationOperation !== 'negative' || !isBoundedNegation(clause, true)) {
127
+ return false;
128
+ }
129
+ }
130
+ return true;
54
131
  }
55
132
 
56
133
  function detectExplicitStrategy(prompt, requestedFableModel) {
@@ -250,6 +327,7 @@ function run(userPrompt, context, options = {}) {
250
327
  /(?:評估|稽核|基準|壓力測試|比較).*(?:每個|所有|全部|整個|全套|技能|skill|檔案|版本)/i,
251
328
  ];
252
329
  const hasMacroSignal = macroSignals.some(signal => signal.test(userPrompt));
330
+ const profileLookup = isFableProfileLookup(userPrompt, hasMacroSignal);
253
331
  const hasTrivialEditVerb = /^(?:(?:please|help me)\s+)?(?:fix|update|correct|change)\b/i.test(userPrompt.trim());
254
332
  const hasDocsTarget = /\b(?:readme|documentation|docs?)\b/i.test(userPrompt);
255
333
  const hasTinyEditSignal = /\b(?:typo|spelling|wording|one line|single line)\b/i.test(userPrompt);
@@ -262,7 +340,12 @@ function run(userPrompt, context, options = {}) {
262
340
  let rationale = 'No structural/testing signals matched; unclassified is not equivalent to trivial.';
263
341
  const reasonCodes = [...loadedConfig.reasonCodes, 'no-classification-signal'];
264
342
 
265
- if (memoryPersistenceRequested && !hasMacroSignal && !hasTier3Keyword && !hasTier2Keyword) {
343
+ if (profileLookup) {
344
+ recommendedTier = 'Tier 1 (Trivial)';
345
+ rationale = 'Bounded behavior-profile lookup; select compact reference before execution.';
346
+ reasonCodes.splice(loadedConfig.reasonCodes.length);
347
+ addReason(reasonCodes, 'fable-profile-lookup');
348
+ } else if (memoryPersistenceRequested && !hasMacroSignal && !hasTier3Keyword && !hasTier2Keyword) {
266
349
  recommendedTier = 'Tier 1 (Trivial)';
267
350
  rationale = 'Explicit bounded self-evolve memory persistence request.';
268
351
  reasonCodes.splice(loadedConfig.reasonCodes.length);
@@ -292,7 +375,7 @@ function run(userPrompt, context, options = {}) {
292
375
  const hasQuestionMarks = /\?|?/.test(userPrompt);
293
376
  const hasMultipleSentences = sentenceCount > 2;
294
377
 
295
- if (!hasMacroSignal && !isTrivialDocsEdit && hasMultipleTasks && hasMultipleSentences) {
378
+ if (!profileLookup && !hasMacroSignal && !isTrivialDocsEdit && hasMultipleTasks && hasMultipleSentences) {
296
379
  if (recommendedTier === 'Unclassified' || recommendedTier.startsWith('Tier 1')) {
297
380
  recommendedTier = 'Tier 2 (Standard Task)';
298
381
  rationale = 'Multiple tasks detected with structural complexity - upgraded to Tier 2 for TDD validation.';
@@ -320,7 +403,7 @@ function run(userPrompt, context, options = {}) {
320
403
  const structuralFloor = hasMacroSignal ? 'tier3'
321
404
  : !isTrivialDocsEdit && hasMultipleTasks && hasMultipleSentences ? 'tier2' : null;
322
405
  const semantic = selectTier({ prompt: userPrompt, tier: recommendedTier, floor: structuralFloor,
323
- explicit: Boolean(detectExplicitStrategy(userPrompt, detectFableModel(userPrompt))) }, process.env, options.systemOneScorer || undefined);
406
+ explicit: profileLookup || Boolean(detectExplicitStrategy(userPrompt, detectFableModel(userPrompt))) }, process.env, options.systemOneScorer || undefined);
324
407
  if (semantic.diagnostic) console.log(`\n=> SYSTEM ONE: ${JSON.stringify(semantic.diagnostic)}`);
325
408
  if (semantic.diagnostic?.applied) {
326
409
  recommendedTier = semantic.tier;
@@ -334,13 +417,18 @@ function run(userPrompt, context, options = {}) {
334
417
 
335
418
  const requestedFableModel = detectFableModel(promptLower);
336
419
  if (requestedFableModel) {
337
- console.log(`\n=> REQUESTED FABLE MODEL MODE: ${requestedFableModel}`);
338
- console.log(`=> ROUTE: fable-mode/fable-${requestedFableModel}/SKILL.md`);
339
- console.log(` Resolve availability and record fallback status with fable-mode/scripts/model-selector.js.`);
420
+ console.log(`\n=> REQUESTED FABLE PROFILE: ${requestedFableModel}`);
421
+ console.log(`=> ROUTE: fable-mode/SKILL.md`);
422
+ if (profileLookup) {
423
+ console.log('=> FABLE OPERATION: profile-lookup');
424
+ console.log('=> REFERENCES: fable-mode/references/profile-lookup.md only; no stage state or delegation.');
425
+ } else {
426
+ console.log(` Profile alias: ${requestedFableModel}; resolve behavior role plus advisory host runtime floor with fable-mode/scripts/model-selector.js.`);
427
+ }
340
428
  }
341
429
 
342
430
  const allRecommendedGuides = [];
343
- for (const group of routingConfig.guideGroups) {
431
+ for (const group of profileLookup ? [] : routingConfig.guideGroups) {
344
432
  let matched = false;
345
433
  if (typeof group.regex === 'string') {
346
434
  matched = new RegExp(group.regex, 'i').test(promptLower);
@@ -366,7 +454,7 @@ function run(userPrompt, context, options = {}) {
366
454
  });
367
455
  }
368
456
 
369
- emitDynamicSkills(promptLower, context, recommendedGuides);
457
+ if (!profileLookup) emitDynamicSkills(promptLower, context, recommendedGuides);
370
458
 
371
459
  const externalClaimTriggers = routingConfig.factAudit.externalClaim || [];
372
460
  const estimateTriggers = routingConfig.factAudit.estimate || [];
@@ -397,7 +485,7 @@ function run(userPrompt, context, options = {}) {
397
485
  const actionGateReasonCodes = detectActionGateReasons(userPrompt);
398
486
  const irreversibleAction = actionGateReasonCodes.includes('irreversible-action');
399
487
  const externalSideEffect = actionGateReasonCodes.includes('external-side-effect');
400
- const requestedStrategy = detectExplicitStrategy(userPrompt, requestedFableModel);
488
+ const requestedStrategy = profileLookup ? null : detectExplicitStrategy(userPrompt, requestedFableModel);
401
489
  const prohibitions = detectProhibitions(userPrompt);
402
490
  const plannerInputs = plannerInputsFromContext(context, promptLower);
403
491
 
package/sbom.cdx.json CHANGED
@@ -2,23 +2,23 @@
2
2
  "$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.7",
5
- "serialNumber": "urn:uuid:29356e45-bc4a-52e1-86a1-e76ba7221fbe",
5
+ "serialNumber": "urn:uuid:284476ec-2aa6-5bc2-883b-7a9196027356",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-10-01T08:19:06.883Z",
8
+ "timestamp": "2026-10-06T16:15:39.747Z",
9
9
  "tools": [
10
10
  {
11
11
  "vendor": "Harness Everything",
12
12
  "name": "generate-sbom.js",
13
- "version": "0.30.4"
13
+ "version": "0.30.6"
14
14
  }
15
15
  ],
16
16
  "component": {
17
17
  "type": "application",
18
- "bom-ref": "pkg:npm/harness-everything@0.30.4",
18
+ "bom-ref": "pkg:npm/harness-everything@0.30.6",
19
19
  "name": "harness-everything",
20
- "version": "0.30.4",
21
- "purl": "pkg:npm/harness-everything@0.30.4"
20
+ "version": "0.30.6",
21
+ "purl": "pkg:npm/harness-everything@0.30.6"
22
22
  }
23
23
  },
24
24
  "components": [
@@ -1999,7 +1999,7 @@
1999
1999
  ]
2000
2000
  },
2001
2001
  {
2002
- "ref": "pkg:npm/harness-everything@0.30.4",
2002
+ "ref": "pkg:npm/harness-everything@0.30.6",
2003
2003
  "dependsOn": [
2004
2004
  "pkg:npm/%40commitlint/cli@19.8.1",
2005
2005
  "pkg:npm/%40commitlint/config-conventional@19.8.1",