synergyspec-selfevolving 1.3.0 → 2.0.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.
Files changed (113) hide show
  1. package/README.md +50 -19
  2. package/dist/commands/learn.d.ts +12 -1
  3. package/dist/commands/learn.js +373 -31
  4. package/dist/commands/self-evolution-episode.d.ts +177 -0
  5. package/dist/commands/self-evolution-episode.js +423 -0
  6. package/dist/commands/self-evolution.d.ts +12 -190
  7. package/dist/commands/self-evolution.js +179 -786
  8. package/dist/commands/workflow/status.js +3 -1
  9. package/dist/core/archive.d.ts +0 -1
  10. package/dist/core/archive.js +0 -58
  11. package/dist/core/artifact-graph/instruction-loader.d.ts +2 -4
  12. package/dist/core/artifact-graph/instruction-loader.js +3 -31
  13. package/dist/core/config-prompts.js +4 -0
  14. package/dist/core/fitness/health/health-metrics.d.ts +26 -56
  15. package/dist/core/fitness/health/health-metrics.js +19 -58
  16. package/dist/core/fitness/health/index.d.ts +15 -2
  17. package/dist/core/fitness/health/index.js +25 -1
  18. package/dist/core/fitness/health/local-source.d.ts +43 -4
  19. package/dist/core/fitness/health/local-source.js +181 -25
  20. package/dist/core/fitness/health/metric-source.d.ts +48 -19
  21. package/dist/core/fitness/health/metric-source.js +8 -18
  22. package/dist/core/fitness/health/resolve-source.js +4 -1
  23. package/dist/core/fitness/loss.d.ts +7 -7
  24. package/dist/core/fitness/loss.js +6 -6
  25. package/dist/core/fitness/sample.d.ts +10 -0
  26. package/dist/core/fitness/test-failures.d.ts +30 -0
  27. package/dist/core/fitness/test-failures.js +123 -0
  28. package/dist/core/learn/credit-path.d.ts +36 -0
  29. package/dist/core/learn/credit-path.js +198 -0
  30. package/dist/core/learn/trajectory-discovery.d.ts +39 -0
  31. package/dist/core/learn/trajectory-discovery.js +140 -0
  32. package/dist/core/learn.d.ts +39 -5
  33. package/dist/core/learn.js +131 -14
  34. package/dist/core/project-config.d.ts +4 -0
  35. package/dist/core/project-config.js +52 -1
  36. package/dist/core/self-evolution/candidate-fitness.d.ts +23 -1
  37. package/dist/core/self-evolution/candidate-fitness.js +31 -5
  38. package/dist/core/self-evolution/candidates.d.ts +0 -9
  39. package/dist/core/self-evolution/canonical-targets.d.ts +8 -4
  40. package/dist/core/self-evolution/canonical-targets.js +8 -4
  41. package/dist/core/self-evolution/critic-agent.d.ts +150 -0
  42. package/dist/core/self-evolution/critic-agent.js +487 -0
  43. package/dist/core/self-evolution/edits-contract.d.ts +53 -0
  44. package/dist/core/self-evolution/edits-contract.js +89 -0
  45. package/dist/core/self-evolution/episode-orchestrator.d.ts +197 -0
  46. package/dist/core/self-evolution/episode-orchestrator.js +534 -0
  47. package/dist/core/self-evolution/episode-store.d.ts +266 -0
  48. package/dist/core/self-evolution/episode-store.js +573 -0
  49. package/dist/core/self-evolution/evolution-switches.d.ts +1 -1
  50. package/dist/core/self-evolution/evolution-switches.js +5 -10
  51. package/dist/core/self-evolution/evolving-agent.d.ts +162 -0
  52. package/dist/core/self-evolution/evolving-agent.js +449 -0
  53. package/dist/core/self-evolution/health-baseline.d.ts +25 -6
  54. package/dist/core/self-evolution/health-baseline.js +30 -6
  55. package/dist/core/self-evolution/host-harness.d.ts +1 -2
  56. package/dist/core/self-evolution/host-harness.js +1 -2
  57. package/dist/core/self-evolution/index.d.ts +10 -6
  58. package/dist/core/self-evolution/index.js +19 -6
  59. package/dist/core/self-evolution/learn-hints.d.ts +31 -0
  60. package/dist/core/self-evolution/learn-hints.js +16 -0
  61. package/dist/core/self-evolution/learn-observation-adapter.d.ts +35 -0
  62. package/dist/core/self-evolution/learn-observation-adapter.js +285 -10
  63. package/dist/core/self-evolution/line-diff.d.ts +60 -0
  64. package/dist/core/self-evolution/line-diff.js +130 -0
  65. package/dist/core/self-evolution/policy/fs-safe.d.ts +19 -0
  66. package/dist/core/self-evolution/policy/fs-safe.js +89 -0
  67. package/dist/core/self-evolution/policy/index.d.ts +13 -0
  68. package/dist/core/self-evolution/policy/index.js +13 -0
  69. package/dist/core/self-evolution/policy/policy-store.d.ts +217 -0
  70. package/dist/core/self-evolution/policy/policy-store.js +774 -0
  71. package/dist/core/self-evolution/policy/reject-buffer.d.ts +48 -0
  72. package/dist/core/self-evolution/policy/reject-buffer.js +168 -0
  73. package/dist/core/self-evolution/promote.d.ts +1 -1
  74. package/dist/core/self-evolution/promote.js +6 -33
  75. package/dist/core/self-evolution/promotion.js +1 -2
  76. package/dist/core/self-evolution/proposer-agent.d.ts +41 -0
  77. package/dist/core/self-evolution/proposer-agent.js +94 -13
  78. package/dist/core/self-evolution/proposer-slice.d.ts +26 -0
  79. package/dist/core/self-evolution/proposer-slice.js +54 -0
  80. package/dist/core/self-evolution/reward-agent.d.ts +234 -0
  81. package/dist/core/self-evolution/reward-agent.js +564 -0
  82. package/dist/core/self-evolution/scope-gate.d.ts +66 -0
  83. package/dist/core/self-evolution/scope-gate.js +107 -0
  84. package/dist/core/self-evolution/success-channel.d.ts +79 -0
  85. package/dist/core/self-evolution/success-channel.js +361 -0
  86. package/dist/core/self-evolution/target-evolution.d.ts +11 -0
  87. package/dist/core/self-evolution/target-evolution.js +2 -0
  88. package/dist/core/self-evolution/tool-evolution.js +2 -13
  89. package/dist/core/self-evolution/verdict.d.ts +8 -5
  90. package/dist/core/self-evolution/verdict.js +4 -7
  91. package/dist/core/templates/skill-templates.d.ts +1 -0
  92. package/dist/core/templates/skill-templates.js +1 -0
  93. package/dist/core/templates/workflow-manifest.js +2 -0
  94. package/dist/core/templates/workflows/learn.d.ts +4 -2
  95. package/dist/core/templates/workflows/learn.js +25 -166
  96. package/dist/core/templates/workflows/self-evolving.d.ts +13 -0
  97. package/dist/core/templates/workflows/self-evolving.js +127 -0
  98. package/dist/core/trajectory/facts.d.ts +16 -0
  99. package/dist/core/trajectory/facts.js +12 -4
  100. package/dist/core/trajectory/skeleton.d.ts +43 -0
  101. package/dist/core/trajectory/skeleton.js +239 -0
  102. package/dist/dashboard/data.d.ts +25 -51
  103. package/dist/dashboard/data.js +68 -180
  104. package/dist/dashboard/react-client.js +458 -503
  105. package/dist/dashboard/react-styles.js +3 -3
  106. package/dist/dashboard/server.js +23 -17
  107. package/dist/ui/ascii-patterns.d.ts +7 -15
  108. package/dist/ui/ascii-patterns.js +123 -54
  109. package/dist/ui/welcome-screen.d.ts +0 -14
  110. package/dist/ui/welcome-screen.js +16 -35
  111. package/package.json +3 -1
  112. package/scripts/code-health.py +1066 -638
  113. package/scripts/slop_rules.yaml +2151 -0
@@ -17,10 +17,25 @@
17
17
  * first run records & allows), and writes are best-effort (a write failure must
18
18
  * never turn a successful promote into an error). Callers supply the timestamp,
19
19
  * so this module never calls Date.now (deterministic + replay-safe).
20
+ *
21
+ * Baselines are SCHEMA-VERSIONED ({@link HEALTH_METRICS_VERSION}): when the
22
+ * meaning/scale of `healthPenalty` changes (e.g. the move from the 7-metric
23
+ * blend to the SlopCodeBench structural_erosion⊕verbosity scores), reading a
24
+ * baseline stamped with a different version returns `null`, so an old-scale
25
+ * number can never mis-gate a new-scale measurement — the gate no-ops and the
26
+ * next promote re-seeds the baseline on the new scale.
20
27
  */
21
28
  import { promises as fs } from 'node:fs';
22
29
  import * as path from 'node:path';
23
30
  export const HEALTH_BASELINE_FILE = 'health-baseline.json';
31
+ /**
32
+ * Schema version of the health-metrics scale behind `healthPenalty`. Bump
33
+ * whenever the meaning/scale of `healthPenalty` changes so stale baselines
34
+ * read as `null` instead of mis-gating. v2 = SlopCodeBench
35
+ * structural_erosion⊕verbosity; v1 (implicit/absent in old files) was the
36
+ * 7-metric blend.
37
+ */
38
+ export const HEALTH_METRICS_VERSION = 2;
24
39
  function baselinePath(repoRoot) {
25
40
  return path.join(path.resolve(repoRoot), '.synergyspec-selfevolving', 'self-evolution', HEALTH_BASELINE_FILE);
26
41
  }
@@ -29,8 +44,10 @@ function isFiniteNumber(v) {
29
44
  }
30
45
  /**
31
46
  * Read the recorded baseline. Returns `null` when the file is absent, unreadable,
32
- * unparseable, or carries a non-finite `healthPenalty` (treated as "no baseline
33
- * yet" the gate does not fire and the first measured run records it).
47
+ * unparseable, carries a non-finite `healthPenalty`, or was stamped with a
48
+ * `metricsVersion` other than {@link HEALTH_METRICS_VERSION} (an absent field
49
+ * a pre-versioning, old-scale baseline). All cases are treated as "no baseline
50
+ * yet" ⇒ the gate does not fire and the next promote re-seeds it.
34
51
  */
35
52
  export async function readHealthBaseline(repoRoot) {
36
53
  let raw;
@@ -52,23 +69,30 @@ export async function readHealthBaseline(repoRoot) {
52
69
  const obj = parsed;
53
70
  if (!isFiniteNumber(obj.healthPenalty))
54
71
  return null;
72
+ if (obj.metricsVersion !== HEALTH_METRICS_VERSION)
73
+ return null;
55
74
  return {
56
75
  healthPenalty: obj.healthPenalty,
57
76
  updatedAt: typeof obj.updatedAt === 'string' ? obj.updatedAt : '',
77
+ metricsVersion: obj.metricsVersion,
58
78
  ...(typeof obj.sourceChange === 'string' ? { sourceChange: obj.sourceChange } : {}),
59
79
  ...(typeof obj.candidateId === 'string' ? { candidateId: obj.candidateId } : {}),
60
80
  };
61
81
  }
62
82
  /**
63
- * Write/overwrite the recorded baseline. Best-effort: creates the parent dir if
64
- * needed and swallows any error (a failed baseline write must not fail a promote
65
- * that already succeeded). Returns `true` on a successful write, `false` otherwise.
83
+ * Write/overwrite the recorded baseline, stamping the current
84
+ * {@link HEALTH_METRICS_VERSION} (callers never supply it, so every written
85
+ * baseline is on the scale this build measures). Best-effort: creates the
86
+ * parent dir if needed and swallows any error (a failed baseline write must not
87
+ * fail a promote that already succeeded). Returns `true` on a successful write,
88
+ * `false` otherwise.
66
89
  */
67
90
  export async function writeHealthBaseline(repoRoot, baseline) {
68
91
  const file = baselinePath(repoRoot);
92
+ const stamped = { ...baseline, metricsVersion: HEALTH_METRICS_VERSION };
69
93
  try {
70
94
  await fs.mkdir(path.dirname(file), { recursive: true });
71
- await fs.writeFile(file, `${JSON.stringify(baseline, null, 2)}\n`, 'utf8');
95
+ await fs.writeFile(file, `${JSON.stringify(stamped, null, 2)}\n`, 'utf8');
72
96
  return true;
73
97
  }
74
98
  catch {
@@ -19,8 +19,7 @@
19
19
  * Back-compat is load-bearing: when the harness resolves to 'claude' (the
20
20
  * default - no CODEX_ or OPENCODE_ env present, as in the unit tests), the built
21
21
  * command is byte-identical to the previous behavior - `binary` + `['-p', prompt]`
22
- * with no stdin - so the existing proposer-agent / replay-runner tests pass
23
- * unchanged.
22
+ * with no stdin - so the existing headless-agent tests pass unchanged.
24
23
  */
25
24
  import { spawn as nodeSpawn } from 'node:child_process';
26
25
  export type AgentHarness = 'claude' | 'codex' | 'opencode';
@@ -19,8 +19,7 @@
19
19
  * Back-compat is load-bearing: when the harness resolves to 'claude' (the
20
20
  * default - no CODEX_ or OPENCODE_ env present, as in the unit tests), the built
21
21
  * command is byte-identical to the previous behavior - `binary` + `['-p', prompt]`
22
- * with no stdin - so the existing proposer-agent / replay-runner tests pass
23
- * unchanged.
22
+ * with no stdin - so the existing headless-agent tests pass unchanged.
24
23
  */
25
24
  import { spawn as nodeSpawn } from 'node:child_process';
26
25
  const HARNESSES = ['claude', 'codex', 'opencode'];
@@ -1,6 +1,5 @@
1
1
  export * from './shared.js';
2
2
  export * from './evolution-switches.js';
3
- export * from './template-variants.js';
4
3
  export * from './archive-memory.js';
5
4
  export * from './task-quality.js';
6
5
  export * from './alignment-verifier.js';
@@ -14,16 +13,21 @@ export * from './candidates.js';
14
13
  export * from './candidate-fitness.js';
15
14
  export * from './verdict.js';
16
15
  export * from './health-baseline.js';
17
- export * from './ga-selection.js';
18
- export * from './trajectory.js';
19
16
  export * from './host-harness.js';
20
- export * from './replay.js';
21
- export * from './replay-runner.js';
22
17
  export * from './credit-assignment.js';
23
18
  export * from './local-targets.js';
24
19
  export * from './promote.js';
25
20
  export * from './candidate-gates.js';
26
21
  export * from './eval-report.js';
27
- export * from './proposer-agent.js';
22
+ export * from './edits-contract.js';
23
+ export * from './proposer-slice.js';
28
24
  export * from './promotion.js';
25
+ export * from './policy/index.js';
26
+ export * from './episode-store.js';
27
+ export * from './line-diff.js';
28
+ export { checkScopeWithinDiagnosis, type ScopeViolation, type ScopeGateResult, type CheckScopeWithinDiagnosisInput, } from './scope-gate.js';
29
+ export * from './critic-agent.js';
30
+ export * from './reward-agent.js';
31
+ export * from './evolving-agent.js';
32
+ export * from './episode-orchestrator.js';
29
33
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,5 @@
1
1
  export * from './shared.js';
2
2
  export * from './evolution-switches.js';
3
- export * from './template-variants.js';
4
3
  export * from './archive-memory.js';
5
4
  export * from './task-quality.js';
6
5
  export * from './alignment-verifier.js';
@@ -14,16 +13,30 @@ export * from './candidates.js';
14
13
  export * from './candidate-fitness.js';
15
14
  export * from './verdict.js';
16
15
  export * from './health-baseline.js';
17
- export * from './ga-selection.js';
18
- export * from './trajectory.js';
19
16
  export * from './host-harness.js';
20
- export * from './replay.js';
21
- export * from './replay-runner.js';
22
17
  export * from './credit-assignment.js';
23
18
  export * from './local-targets.js';
24
19
  export * from './promote.js';
25
20
  export * from './candidate-gates.js';
26
21
  export * from './eval-report.js';
27
- export * from './proposer-agent.js';
22
+ export * from './edits-contract.js';
23
+ export * from './proposer-slice.js';
28
24
  export * from './promotion.js';
25
+ // ── Loop v2 (self-evolution as in-context RL) ────────────────────────────────
26
+ // Policy (ledger + reject-buffer + fs-safe), the disk episode store, the three
27
+ // agents (critic / reward / evolving), the line-diff + scope gate, and the
28
+ // rollback-before-evolution episode orchestrator. ArmObjective is the SINGLE
29
+ // canonical on-disk type from critic-agent.js; reward-agent.js reads it via its
30
+ // own null-safe RewardArmObjectiveInput, so this barrel has no ambiguous re-export.
31
+ export * from './policy/index.js';
32
+ export * from './episode-store.js';
33
+ export * from './line-diff.js';
34
+ // scope-gate.js also declares a DiagnosisGap ({file, section}); the richer
35
+ // reward-agent.js DiagnosisGap ({file, section, description}) is the one the
36
+ // barrel surfaces. Re-export the scope gate WITHOUT its DiagnosisGap.
37
+ export { checkScopeWithinDiagnosis, } from './scope-gate.js';
38
+ export * from './critic-agent.js';
39
+ export * from './reward-agent.js';
40
+ export * from './evolving-agent.js';
41
+ export * from './episode-orchestrator.js';
29
42
  //# sourceMappingURL=index.js.map
@@ -12,6 +12,7 @@
12
12
  * has no side effects, no dependencies, and never mutates inputs.
13
13
  */
14
14
  import type { CanonicalTargetKind } from './canonical-targets.js';
15
+ import type { CreditPath } from '../learn/credit-path.js';
15
16
  /**
16
17
  * Closed enumeration of proposed change types a learn-stage hint may carry.
17
18
  *
@@ -58,6 +59,20 @@ export interface LearnEvolutionHintEvidence {
58
59
  quoteOrSummary: string;
59
60
  /** Severity of the observation in isolation. */
60
61
  severity: 'low' | 'medium' | 'high' | 'critical';
62
+ /**
63
+ * Where the evidence came from: `'observed'` = re-sourced from the observed
64
+ * runner output (the source the observed-verified gate trusts);
65
+ * `'authored'` = grepped from agent-authored artifacts. OPTIONAL — absent on
66
+ * legacy persisted hints (treated as authored).
67
+ */
68
+ provenance?: 'observed' | 'authored';
69
+ /**
70
+ * Rendered credit-path address from the failing signal back to the suspected
71
+ * producing canonical target, e.g. `test … → UC1-S2 → task 3.2 → design
72
+ * §"Normalizer" → artifact-template:design`. SUSPECT framing — recurrence
73
+ * across changes (the grouping machinery) is the confirmer. OPTIONAL.
74
+ */
75
+ address?: string;
61
76
  }
62
77
  /**
63
78
  * Structured hint emitted by `learn` for downstream candidate proposal.
@@ -100,6 +115,22 @@ export interface LearnEvolutionHint {
100
115
  * todo/learn-self-evolution-migration-plan.md.
101
116
  */
102
117
  loss?: number;
118
+ /**
119
+ * Full neutral credit paths (hops + bounded real-text excerpts) behind the
120
+ * evidence items' rendered `address` strings — the substrate the proposer
121
+ * slice is built from. Optional; absent on legacy persisted hints.
122
+ */
123
+ creditPaths?: CreditPath[];
124
+ /**
125
+ * `'policy-focus'` marks a hint SYNTHESIZED by the evolution-focus switch:
126
+ * every heuristic hint of this change bound to a frozen kind, so their
127
+ * evidence was re-aimed at an explicitly evolvable target instead of being
128
+ * silently dropped. SUSPECT framing — the hint asks the proposer to evaluate
129
+ * whether the focused target could prevent the signals' recurrence; it does
130
+ * NOT claim the focused target caused them. Optional; absent on ordinary
131
+ * heuristic hints and on legacy persisted hints.
132
+ */
133
+ origin?: 'policy-focus';
103
134
  }
104
135
  /**
105
136
  * Result of `validateLearnEvolutionHint`.
@@ -76,6 +76,14 @@ function validateEvidenceItem(item, index, errors) {
76
76
  else if (!EVIDENCE_SEVERITY_SET.has(item.severity)) {
77
77
  errors.push(`${path}.severity must be one of low|medium|high|critical (got "${item.severity}")`);
78
78
  }
79
+ // Optional fields are validated ONLY when present, so legacy persisted hints
80
+ // (which predate them) keep validating unchanged.
81
+ if (item.provenance !== undefined && item.provenance !== 'observed' && item.provenance !== 'authored') {
82
+ errors.push(`${path}.provenance must be 'observed' or 'authored' when present`);
83
+ }
84
+ if (item.address !== undefined) {
85
+ pushNonEmptyStringError(errors, `${path}.address`, item.address);
86
+ }
79
87
  }
80
88
  /**
81
89
  * Runtime contract enforcer for `LearnEvolutionHint`.
@@ -144,6 +152,14 @@ export function validateLearnEvolutionHint(value) {
144
152
  if (value.loss !== undefined && (typeof value.loss !== 'number' || Number.isNaN(value.loss))) {
145
153
  errors.push('loss must be a number when present');
146
154
  }
155
+ // Optional, validated only when present (legacy persisted hints lack it).
156
+ if (value.creditPaths !== undefined && !Array.isArray(value.creditPaths)) {
157
+ errors.push('creditPaths must be an array when present');
158
+ }
159
+ // Optional, validated only when present (ordinary heuristic hints lack it).
160
+ if (value.origin !== undefined && value.origin !== 'policy-focus') {
161
+ errors.push(`origin must be 'policy-focus' when present (got "${String(value.origin)}")`);
162
+ }
147
163
  if (errors.length > 0) {
148
164
  return { ok: false, errors };
149
165
  }
@@ -1,5 +1,6 @@
1
1
  import { type LearnEvolutionHint } from './learn-hints.js';
2
2
  import { type TargetEvolutionPolicy } from './target-evolution.js';
3
+ import { type CreditPath } from '../learn/credit-path.js';
3
4
  import { type LearnReport, type LearnObservation } from '../learn.js';
4
5
  /** The learn signals the interpreter reads (everything except the neutral observations). */
5
6
  type LearnSignals = Omit<LearnReport, 'observations'>;
@@ -24,6 +25,8 @@ type LearnSignals = Omit<LearnReport, 'observations'>;
24
25
  * should still wrap in try/catch and fall back to the registry source path.
25
26
  */
26
27
  export declare function resolveTargetLocalFilesReadonly(targetId: string, repoRoot: string): Promise<string[]>;
28
+ /** Raw producing-target id for a path's last node (no policy annotation). */
29
+ export declare function producingTargetIdForCreditPath(creditPath: CreditPath): string | undefined;
27
30
  /**
28
31
  * Derive structured evolution hints from a learn report's signals.
29
32
  *
@@ -47,6 +50,20 @@ export declare function generateEvolutionHints(report: LearnSignals, policy?: Ta
47
50
  * hint entirely (no evolvable target of its kind exists).
48
51
  */
49
52
  export declare function resolveKindOnlyPinTarget(draft: LearnEvolutionHint, policy: TargetEvolutionPolicy): string | undefined | null;
53
+ /**
54
+ * The evolution-focus target ids: the explicitly evolvable, REGISTERED
55
+ * canonical targets of a frozen-by-default policy, with the focus switch ON.
56
+ *
57
+ * Empty (focus inactive) when:
58
+ * - the switch is off (`selfEvolution.focus: false` / `learn --no-focus`),
59
+ * - the policy default is `evolvable` (nothing is dropped as frozen-kind in the
60
+ * way focus rescues — and "evolve everything" needs no focus), or
61
+ * - no explicit target survives (freeze-wins already resolved into `explicit`).
62
+ *
63
+ * Exported for the learn command's transparency rendering, so the printed focus
64
+ * line cannot drift from the hint-synthesis pass.
65
+ */
66
+ export declare function resolveEvolutionFocusTargets(policy: TargetEvolutionPolicy | undefined): string[];
50
67
  /**
51
68
  * Surface an AMBIGUOUS kind-only evolution hint as an action-required
52
69
  * observation. After {@link scopeHintsByPolicy} runs, a hint that still has no
@@ -63,12 +80,30 @@ export declare function resolveKindOnlyPinTarget(draft: LearnEvolutionHint, poli
63
80
  * unbindable, keeping learn output byte-identical in the common case.
64
81
  */
65
82
  export declare function detectUnbindableHintObservations(hints: LearnEvolutionHint[], policy: TargetEvolutionPolicy | undefined): LearnObservation[];
83
+ /**
84
+ * Surface health offenders whose routing lands on a FROZEN target as ONE
85
+ * aggregated ACTION-REQUIRED observation — the honest dead-end. Without this,
86
+ * a freezing policy makes the offender signal vanish silently: the enrichment
87
+ * in {@link generateEvolutionHints} only rides on drafts that survive scoping.
88
+ * Mirrors {@link detectUnbindableHintObservations}' shape and wiring (call it
89
+ * wherever that one is called, on evolving runs). Returns `[]` when `policy`
90
+ * is absent or the routing target is evolvable — the enrichment path already
91
+ * carries the signal there — so the common case stays byte-identical.
92
+ */
93
+ export declare function detectFrozenHealthRoutingObservations(report: LearnSignals, policy?: TargetEvolutionPolicy): LearnObservation[];
66
94
  /**
67
95
  * Persist hints to the canonical handoff path that `propose-canonical
68
96
  * --from-learn` reads (`{ evolutionHints: [...] }`). This is the one-motion
69
97
  * learn → propose bridge: it writes an ARTIFACT under `learn-handoffs/` (never a
70
98
  * canonical file), so it is proposal-only. Reuses the existing
71
99
  * `learn-handoffs/<change>/<timestamp>/` convention. Returns the file path.
100
+ *
101
+ * When any hint carries credit paths, a sibling `slice.md` is written next to
102
+ * `hints.json` (and referenced via the optional top-level `slice` key) so the
103
+ * HOST author on the `--from-edits` path can read the real artifact text along
104
+ * the failing paths — the same CREDIT-PATH SLICE the `--agent` prompt renders.
105
+ * Old readers ignore unknown top-level keys (`extractHintCandidates` reads only
106
+ * the hint arrays), so the addition is back-compatible.
72
107
  */
73
108
  export declare function persistLearnHints(opts: {
74
109
  projectRoot: string;