fix(regression): repair 5 regressions from second round, close B10/B12/B15/B16
Round 2 regression fixes: - B10 (INV-2 outbox, CRITICAL): wiring.ts — switch durable events from eventBus.publish (live-only) to eventIngestor.ingest (persistent) for debug.record.created and memory.promoted. Add required RuntimeEvent fields (id, source, route). - B12 (Scheduler events, CRITICAL): Scheduler.ts — replace all 4 eventBus.publish calls with eventIngestor.ingest + registered event types (task.started/task.failed/agent.lost/agent.cancelled). Remove unregistered task.status.changed references. - B15 (duplicate ProjectionClient): remove orphan tui/src/ProjectionClient.ts (zero references, superseded by runtime/src/projection/ProjectionClient.ts re-exported via @aircoding/runtime barrel). - RuntimeApp: wire Scheduler→WorkerManager in constructor; document start() bootstrap→recover→hydrate→ready sequence (DD §22.2). - createRuntime: read project_id from .air/shared/project.json (DD §6.1 stable UUID), fallback to Date.now() only if not initialized. - B16 (api_key strict): ProviderManager.get_or_create_adapter now calls ModelConfigLoader.validate() before passing raw api_key to adapter. Also fix from R1 regression: - ArchitectureDesigner: replace broken additive-heuristic risk scoring (single runtime file→replan, large refactor→confirmation only) with change-scope classification (contracts→confirmation, breaking→escalate, large→replan, safe→silent_continue). Remove dead evaluate_risk(). - MainAgent test: update confirmation test from old state name AWAITING_CONFIRMATION to canonical CONFIRMING (B13 state machine fix). Test: 148/148 pass (regression + e2e + llm + toolchain-cpp). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,9 +25,6 @@ export class ArchitectureDesigner {
|
||||
// Analyze which components are affected
|
||||
const affected = this.identify_affected_components(change.files)
|
||||
|
||||
// Determine result class
|
||||
const risk_level = this.evaluate_risk(change, affected)
|
||||
|
||||
const impact: ArchitectureImpact = {
|
||||
result: 'silent_continue',
|
||||
affected_components: affected,
|
||||
@@ -36,15 +33,27 @@ export class ArchitectureDesigner {
|
||||
requires_replan: false
|
||||
}
|
||||
|
||||
if (risk_level >= 4) {
|
||||
// Classify by change scope, not mere package membership (DD §19.4):
|
||||
// - contract/interface change → user confirmation (breaking → escalate)
|
||||
// - broad multi-file change → replan
|
||||
// - otherwise → silent_continue
|
||||
const is_breaking = /deprecat|break|remove/.test(change.description.toLowerCase())
|
||||
const touches_contracts = affected.includes('contracts')
|
||||
const is_large = change.files.length > 10
|
||||
|
||||
if (touches_contracts && is_breaking) {
|
||||
impact.result = 'reject_or_escalate'
|
||||
impact.risks.push('High architectural risk')
|
||||
} else if (risk_level >= 3) {
|
||||
impact.risks.push('Breaking change to frozen contracts')
|
||||
} else if (touches_contracts) {
|
||||
impact.result = 'requires_user_confirmation'
|
||||
impact.risks.push('Moderate impact on architecture')
|
||||
} else if (risk_level >= 2) {
|
||||
impact.risks.push('Contract/interface surface change')
|
||||
} else if (is_large) {
|
||||
impact.result = 'requires_replan'
|
||||
impact.requires_replan = true
|
||||
impact.risks.push('Large multi-file change requires re-planning')
|
||||
} else if (is_breaking) {
|
||||
impact.result = 'requires_user_confirmation'
|
||||
impact.risks.push('Potentially breaking change')
|
||||
}
|
||||
|
||||
return impact
|
||||
@@ -73,13 +82,4 @@ export class ArchitectureDesigner {
|
||||
}
|
||||
return [...new Set(components)]
|
||||
}
|
||||
|
||||
private evaluate_risk(change: { description: string; files: string[] }, affected: string[]): number {
|
||||
let risk = 0
|
||||
if (affected.includes('contracts')) risk += 3 // Contract changes are high risk
|
||||
if (affected.includes('runtime')) risk += 2
|
||||
if (change.files.length > 10) risk += 1
|
||||
if (/deprecat|break|remove/.test(change.description.toLowerCase())) risk += 2
|
||||
return risk
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,15 @@ export type MainAgentState =
|
||||
| 'DELEGATING'
|
||||
| 'DIRECT_MODE'
|
||||
| 'SCHEDULING'
|
||||
| 'AWAITING'
|
||||
| 'ARCHITECTURE_DESIGNING'
|
||||
| 'CONFIRMING'
|
||||
| 'EXECUTING'
|
||||
| 'INTERRUPTING'
|
||||
| 'ARCHITECTURE_REVISING'
|
||||
| 'SUMMARIZING'
|
||||
| 'ERROR'
|
||||
| 'TERMINATED'
|
||||
|
||||
export interface MainAgentConfig {
|
||||
session_id: SessionID
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import { DebugKnowledgeStore } from '../knowledge/DebugKnowledgeStore.js'
|
||||
import { LearnedMemoryStore } from '../knowledge/LearnedMemoryStore.js'
|
||||
import { eventIngestor } from '../events/EventIngestor.js'
|
||||
|
||||
export interface KnowledgeWiring {
|
||||
debug_store: DebugKnowledgeStore
|
||||
@@ -61,7 +62,25 @@ export async function capture_debug_record(
|
||||
updated_at: now,
|
||||
metadata_json: record.metadata_json,
|
||||
})
|
||||
// INV-2: emit debug.record.created event AFTER external write
|
||||
// INV-2: emit debug.record.created (durable) via EventIngestor AFTER external write
|
||||
await eventIngestor.ingest({
|
||||
id: record.id,
|
||||
type: 'debug.record.created',
|
||||
version: 1,
|
||||
session_id: record.task_id,
|
||||
project_id: '',
|
||||
timestamp: now,
|
||||
source: { kind: 'agent', agent_type: 'debugger' },
|
||||
route: ['knowledge', 'debug'],
|
||||
payload: {
|
||||
debug_record_id: record.id,
|
||||
task_id: record.task_id,
|
||||
failure_signature: record.failure_signature,
|
||||
summary: record.summary,
|
||||
evidence_refs: [],
|
||||
verification_refs: [],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,5 +113,21 @@ export async function promote_memory_entry(
|
||||
updated_at: now,
|
||||
metadata_json: entry.metadata_json,
|
||||
})
|
||||
// INV-2: emit memory.promoted event AFTER external write
|
||||
// INV-2: emit memory.promoted (durable) via EventIngestor AFTER external write
|
||||
await eventIngestor.ingest({
|
||||
id: entry.id,
|
||||
type: 'memory.promoted',
|
||||
version: 1,
|
||||
session_id: entry.source_entity_id || '',
|
||||
project_id: '',
|
||||
timestamp: now,
|
||||
source: { kind: 'agent', agent_type: 'experience_miner' },
|
||||
route: ['knowledge', 'memory'],
|
||||
payload: {
|
||||
candidate_id: entry.id,
|
||||
target_ref: entry.source_entity_type || '',
|
||||
promoted_by: 'experience_miner',
|
||||
summary: entry.summary,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user