kenaz 0.3.10 โ 0.3.12
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/core/.claude/skills/code_reviewer/SKILL.md +0 -1
- package/core/.claude/skills/code_reviewer/patterns.yaml +0 -8
- package/core/.claude/skills/dev_agent/SKILL.md +3 -6
- package/core/.claude/skills/heimdall/SKILL.md +5 -6
- package/core/.claude/skills/huginn/SKILL.md +11 -38
- package/core/.collaboration/core/task-2288-followups.test.js +2 -37
- package/core/.collaboration/core/task-2421-replica.test.js +229 -0
- package/core/.collaboration/core/task-api.js +223 -230
- package/core/.collaboration/core/task-api.test.js +1 -138
- package/core/.collaboration/core/task-replica.js +189 -0
- package/core/.formation/.merge-lock +1 -0
- package/core/.formation/bifrost/codex-doctor.js +12 -2
- package/core/.formation/bifrost/codex-doctor.test.js +15 -0
- package/core/.formation/bifrost/launch.js +2 -2
- package/core/.formation/bifrost/launch.test.js +2 -2
- package/core/.formation/bifrost/setup-codex.js +84 -16
- package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
- package/core/.formation/bifrost/shared/codex-injection.js +3 -17
- package/core/.formation/bifrost/shared/consolidate.js +1 -51
- package/core/.formation/bifrost/sync.js +4 -5
- package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +1 -1
- package/core/.formation/tools/dispatch.js +2 -2
- package/core/.formation/tools/kenaz-stats.js +1 -1
- package/core/.formation/tools/mcp-allowlist.test.js +5 -3
- package/core/.formation/tools/post-complete/bounded-http.js +1 -40
- package/core/.formation/tools/post-complete/bounded-http.test.js +2 -89
- package/core/.formation/tools/post-complete/cognitive.js +2 -119
- package/core/.formation/tools/post-complete/task2420.test.js +129 -0
- package/core/.formation/tools/post-complete/testing.js +3 -21
- package/core/.formation/tools/post-complete.js +131 -436
- package/core/.formation/tools/post-complete.test.js +0 -135
- package/core/.formation/tools/route-audit.test.js +8 -20
- package/core/.kenaz/specialist/memory_guard_fired.json +1 -1
- package/core/.kenaz/specialist/memory_guard_trigger_cache.json +1 -1
- package/core/.rule/INDEX.yaml +3 -3
- package/core/.rule/general/ddx-synapse-stack.yaml +11 -11
- package/core/.rule/general/specialist-identity-contract.yaml +5 -5
- package/core/.rule/general/synapse-code-intelligence.yaml +25 -111
- package/core/.rule/general/worktree-isolation.yaml +2 -2
- package/core/.rule/pipeline/task-execution.yaml +6 -9
- package/core/.specialist/Amelia/README.md +5 -5
- package/core/.specialist/Amelia/capability.yaml +10 -18
- package/core/.specialist/Amelia/knowledge.yaml +15 -66
- package/core/.specialist/Amelia/workflow.yaml +12 -21
- package/core/.specialist/Ansuz/capability.yaml +12 -87
- package/core/.specialist/Ansuz/concepts/autonomous_agenda.yaml +1 -1
- package/core/.specialist/Ansuz/concepts/autonomy_contract.yaml +2 -3
- package/core/.specialist/Ansuz/concepts/capability_pointers.yaml +1 -3
- package/core/.specialist/Ansuz/concepts/cognitive_fluency_contract.yaml +8 -21
- package/core/.specialist/Ansuz/concepts/cognitive_systems_overview.yaml +0 -4
- package/core/.specialist/Ansuz/concepts/cognitive_systems_turn_context.yaml +1 -1
- package/core/.specialist/Ansuz/concepts/completion_tracking.yaml +0 -1
- package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +0 -3
- package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +3 -3
- package/core/.specialist/Ansuz/concepts/fatigue_handoff.yaml +1 -7
- package/core/.specialist/Ansuz/concepts/guardrails_immutable.yaml +3 -4
- package/core/.specialist/Ansuz/concepts/identity.yaml +1 -2
- package/core/.specialist/Ansuz/concepts/phase9_verification.yaml +0 -1
- package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +3 -4
- package/core/.specialist/Ansuz/core.yaml +3 -6
- package/core/.specialist/Ansuz/elixir.yaml +6 -6
- package/core/.specialist/Ansuz/exo_cortex_tools.yaml +5 -9
- package/core/.specialist/Ansuz/guards/blind_spot_guards.yaml +2 -2
- package/core/.specialist/Ansuz/guards/lesson_guards.yaml +6 -19
- package/core/.specialist/Ansuz/identity_contract.yaml +4 -4
- package/core/.specialist/Ansuz/knowledge.yaml +13 -40
- package/core/.specialist/Ansuz/modules/ddx.yaml +2 -2
- package/core/.specialist/Ansuz/modules/prophecy.yaml +19 -19
- package/core/.specialist/Ansuz/modules/pulse_empty.yaml +6 -17
- package/core/.specialist/Ansuz/tools/ansuz-handoff-snapshot.js +3 -102
- package/core/.specialist/Ansuz/tools/ansuz-sdk-runner.js +2 -44
- package/core/.specialist/Ansuz/workflow.yaml +32 -76
- package/core/.specialist/Forseti/core.yaml +2 -7
- package/core/.specialist/Forseti/rule.yaml +1 -1
- package/core/.specialist/Heimdall/SKILL.md +2 -3
- package/core/.specialist/Heimdall/capability.yaml +22 -45
- package/core/.specialist/Heimdall/core.yaml +24 -126
- package/core/.specialist/Heimdall/principles.yaml +8 -11
- package/core/.specialist/Heimdall/rule.yaml +2 -2
- package/core/.specialist/Huginn/capability.yaml +31 -52
- package/core/.specialist/Huginn/core.yaml +43 -123
- package/core/.specialist/Huginn/principles.yaml +5 -5
- package/core/.specialist/Huginn/rule.yaml +3 -9
- package/core/.specialist/REFLECTION_TO_CONTROL.md +3 -4
- package/core/.specialist/shared/guard_schema.json +1 -1
- package/core/.specialist/shared/principles.yaml +6 -6
- package/core/.specialist/tools/__tests__/rea-detect.test.js +10 -0
- package/core/.specialist/tools/phase-gate.js +10 -230
- package/core/.specialist/tools/phase-gate.test.js +2 -2
- package/core/.specialist/tools/prompt-files.test.js +1 -1
- package/core/.specialist/tools/rea-detect.js +6 -2
- package/core/.specialist/tools/session.js +2 -89
- package/core/.synapse/memory-shared.json.sync +1 -0
- package/core/.system/memory/README.md +3 -0
- package/core/CLAUDE.md +6 -7
- package/core/KENAZ_CORE_VERSION +1 -1
- package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +75 -83
- package/core/dev/specs/peer/CODEX_MANAGED_DAEMON.md +54 -0
- package/core/dev/specs/peer/setup-codex-hooks.test.js +38 -0
- package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +56 -0
- package/core/dev/spikes/task2145-evidence-20261004/graph.shadow +0 -0
- package/core/dev/spikes/task2145-evidence-20261004/graph.wal +0 -0
- package/core/dev/spikes/task2145-evidence-20261004/graph_corrupt_suspect.marker +1 -0
- package/core/plugins/kenaz/INVENTORY.md +1 -1
- package/core/plugins/kenaz/scripts/kenaz.test.js +70 -1
- package/core/plugins/kenaz/scripts/ygg.js +45 -4
- package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +0 -1
- package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +0 -8
- package/core/plugins/kenaz/skills/dev_agent/SKILL.md +3 -6
- package/lib/cli.js +17 -9
- package/lib/cli.test.js +10 -0
- package/lib/dashboard.test.js +3 -3
- package/package.json +6 -6
- package/vendor/install-kenaz-cli.js +167 -16
- package/core/.claude/hooks/handoff-snapshot-on-decision.js +0 -188
- package/core/.claude/skills/synapse_maintain/SKILL.md +0 -84
- package/core/.collaboration/core/task-2264-single-after-capture.test.js +0 -61
- package/core/.formation/tools/alaya-gate.js +0 -198
- package/core/.formation/tools/post-complete/health.js +0 -464
- package/core/.formation/tools/post-complete/task2038.test.js +0 -246
- package/core/.formation/tools/post-complete/task2341.test.js +0 -74
- package/core/.rule/general/cortex-tools.yaml +0 -181
- package/core/.rule/general/memory-language.yaml +0 -67
- package/core/.rule/general/memory-source-priority.yaml +0 -207
- package/core/.rule/general/synapse-cognitive-fusion.yaml +0 -301
- package/core/.specialist/Ansuz/concepts/cognitive_systems_hippocampus.yaml +0 -14
- package/core/.specialist/Ansuz/concepts/write_verification.yaml +0 -26
- package/core/.specialist/Ansuz/modules/recall.yaml +0 -45
- package/core/.specialist/tools/MEMORY_RECALL_TRIGGERS.md +0 -156
- package/core/.specialist/tools/context-injection.js +0 -395
- package/core/dev/scripts/probe/hippocampus-concurrency.js +0 -247
|
@@ -43,24 +43,18 @@
|
|
|
43
43
|
* |-------|------------------------------------------|-------------------------------------------|----------------------------|-------------------------------------|
|
|
44
44
|
* | GC | gcStaleWorktrees() | always | try/catch (non-fatal) | orphan worktrees accumulate |
|
|
45
45
|
* | Guard | Duplicate-suppress guard | recent worktree pass for same taskId (<2m) | try/catch (non-fatal) | double execution |
|
|
46
|
-
* | 0 | Hippocampus episode start | taskId && !dryRun | try/catch (non-fatal) | episodes not started |
|
|
47
46
|
* | 1 | archiveCheck() | !skipArchive | inline logging | none โ optional |
|
|
48
47
|
* | 2 | gitBranchAndMerge() or _gitMergeFromWorktree() | !skipGit && taskId | throws (caught at CLI) | task stuck in active |
|
|
49
48
|
* | 2.5a | Yggdrasil commit-sync (fire-and-forget) | gitResult.committed && !dryRun | catch (silent) | sync missed silently |
|
|
50
|
-
* | 2.6a | Passive rune extraction | gitResult.committed && !dryRun | catch (silent) | runes not extracted |
|
|
51
49
|
* | 2.7 | Outcome recording + surprise detection | !dryRun | catch (silent) | no outcome metrics recorded |
|
|
52
50
|
* | 2.8 | Skill matrix update | !dryRun | catch (log warning) | metrics not updated |
|
|
53
51
|
* | 2.9 | Cognitive attribution | !dryRun && mergeSucceeded | catch (silent) | attribution missed |
|
|
54
52
|
* | 2.5b | Complete/block task via task-api | merge success or failure | spawnSync; safety net move | task stuck in active |
|
|
55
53
|
* | 2.5c | Record AC failures (ACCEPTANCE_FAILED) | acFailures.length > 0 | catch (log) | AC failures invisible |
|
|
56
54
|
* | 2.5d | Safety net: force-move to done | merge succeeded but complete() failed | spawnSync | task stays in active |
|
|
57
|
-
* | 2.5e | Passive rune extraction (completed) | result.taskCompleted && !dryRun | catch (silent) | completion runes not tagged |
|
|
58
55
|
* | 2.6b | Ansuz inbox signal | result.taskCompleted && !dryRun | catch (silent) | Ansuz not notified |
|
|
59
56
|
* | 2.6c | Relay task_completed event (TASK_1409) | result.taskCompleted && !dryRun && relay | catch (silent) | PM inbox not notified cross-node |
|
|
60
57
|
* | 3 | Memory agent onTaskCompleted/onError | memoryAgent && taskId | catch (log warning) | memory event not queued |
|
|
61
|
-
* | 3b | Provenance runes (synapse_inscribe) | !dryRun && git.merged | catch (silent) | provenance not tagged |
|
|
62
|
-
* | 4 | Guardian health check + KEEP/REVERT | taskId (always) | catch (log) | no feedback signal for loop |
|
|
63
|
-
* | 6 | Hippocampus episode close | hippocampusEpisodeId set | catch (log warning) | episode not closed properly |
|
|
64
58
|
*
|
|
65
59
|
* A.2 gitBranchAndMerge() โ serial mode (L2344)
|
|
66
60
|
*
|
|
@@ -333,7 +327,6 @@ try {
|
|
|
333
327
|
|
|
334
328
|
const CONFIG = {
|
|
335
329
|
DEFAULT_KEEP: 10,
|
|
336
|
-
GUARDIAN_INTERVAL: 10, // Run synapse_health snapshot every N completed tasks
|
|
337
330
|
};
|
|
338
331
|
|
|
339
332
|
// ?????????????????????????????????????????????????????????????????????????????
|
|
@@ -1199,20 +1192,15 @@ function gcStaleWorktrees(currentTaskId, projectPath) {
|
|
|
1199
1192
|
|
|
1200
1193
|
// ============================================================================
|
|
1201
1194
|
// Cognitive tool usage tracking (TASK_1105)
|
|
1202
|
-
// Records which cognitive tools (
|
|
1195
|
+
// Records which cognitive tools (kenaz_lsp_*, ddx, etc.) were
|
|
1203
1196
|
// invoked during the task, aggregates into .kenaz/specialist/ansuz_self_model.json
|
|
1204
1197
|
// under cognitive_competence, and flags negative-trend tools as mirror blind spots.
|
|
1205
1198
|
// ============================================================================
|
|
1206
1199
|
|
|
1207
1200
|
const COGNITIVE_TOOLS = [
|
|
1208
|
-
'synapse_recall',
|
|
1209
1201
|
'kenaz_lsp_callers',
|
|
1210
1202
|
'kenaz_lsp_references',
|
|
1211
|
-
'synapse_inscribe',
|
|
1212
|
-
'synapse_forge',
|
|
1213
1203
|
'ddx',
|
|
1214
|
-
'hippocampus_episode_start',
|
|
1215
|
-
'cortex_recall',
|
|
1216
1204
|
];
|
|
1217
1205
|
|
|
1218
1206
|
function sleepSync(ms) {
|
|
@@ -1778,6 +1766,98 @@ function classifyMergeFailureReason(g) {
|
|
|
1778
1766
|
return 'git_step_failed';
|
|
1779
1767
|
}
|
|
1780
1768
|
|
|
1769
|
+
/**
|
|
1770
|
+
* TASK_2420: the real cause of a failed spawnSync. stderr/stdout first, then the spawn
|
|
1771
|
+
* error (ENOENT / ETIMEDOUT), then the signal, then the exit code โ never a bare "unknown".
|
|
1772
|
+
*/
|
|
1773
|
+
function spawnFailureCause(res) {
|
|
1774
|
+
if (!res) return 'no result from child process';
|
|
1775
|
+
const text = (res.stderr || res.stdout || '').toString().trim();
|
|
1776
|
+
if (text) return text.split('\n')[0];
|
|
1777
|
+
if (res.error) return `${res.error.code ? res.error.code + ': ' : ''}${res.error.message}`;
|
|
1778
|
+
if (res.signal) return `child killed by signal ${res.signal} (timeout?)`;
|
|
1779
|
+
return `child exited with code ${res.status} and no output`;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
/**
|
|
1783
|
+
* TASK_2420: git step that has nothing to integrate (task declared source-less: runtime /
|
|
1784
|
+
* config repair). Never skips when real work exists: a worktree or a task/<id> branch that
|
|
1785
|
+
* is ahead of the user branch means there IS something to merge, so that is a failure.
|
|
1786
|
+
*/
|
|
1787
|
+
function buildNoSourceGitResult(taskId, projectPath, worktreePath) {
|
|
1788
|
+
const skipReason = 'no branch: nothing to integrate';
|
|
1789
|
+
const taskBranch = `task/${taskId.toLowerCase()}`;
|
|
1790
|
+
const root = resolveProjectRoot(projectPath);
|
|
1791
|
+
const refuse = (why) => ({
|
|
1792
|
+
taskBranch, userBranch: null, originalBranch: null, committed: false, merged: false,
|
|
1793
|
+
verify_merge_landed: false, failure_reason: 'no_source_but_work_exists', blockedReason: why,
|
|
1794
|
+
});
|
|
1795
|
+
if (worktreePath) return refuse(`--no-source given but worktree ${worktreePath} exists; merge it or drop --no-source`);
|
|
1796
|
+
if (root && branchExists(taskBranch, root)) {
|
|
1797
|
+
const userBranch = resolveUserBranch(root);
|
|
1798
|
+
const ahead = userBranch ? git(`rev-list ${userBranch}..${taskBranch}`, { cwd: root, throws: false }) : null;
|
|
1799
|
+
if (ahead === null || ahead.trim()) {
|
|
1800
|
+
return refuse(`--no-source given but ${taskBranch} exists with commits not on ${userBranch}; merge it or drop --no-source`);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return { taskBranch: null, userBranch: null, originalBranch: null, committed: false, merged: false,
|
|
1804
|
+
noChanges: true, gitSkipped: true, noSource: true, gitSkipReason: 'no_source', gitSkipDetail: skipReason };
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* TASK_2420: truthful per-step summary of a postComplete() result.
|
|
1809
|
+
* outcome: 'completed' | 'completed_with_warnings' | 'not_completed'.
|
|
1810
|
+
* Semantics: a git step that failed means the card must NOT be reported completed โ it is
|
|
1811
|
+
* moved to blocked/ (existing behaviour) and `cardState` says where it really is. Failures
|
|
1812
|
+
* after the merge landed (card bookkeeping) leave code delivered, so they are warnings.
|
|
1813
|
+
*/
|
|
1814
|
+
function summarizePostComplete(result) {
|
|
1815
|
+
const steps = [];
|
|
1816
|
+
const warnings = [];
|
|
1817
|
+
const g = result.git;
|
|
1818
|
+
let gitFailed = false;
|
|
1819
|
+
if (!g) {
|
|
1820
|
+
steps.push({ step: 'git', status: 'skipped', reason: 'git step not run' });
|
|
1821
|
+
} else if (isVerifiedMergeSuccess(g)) {
|
|
1822
|
+
steps.push({ step: 'git', status: 'ok' });
|
|
1823
|
+
} else if (g.noSource) {
|
|
1824
|
+
steps.push({ step: 'git', status: 'skipped', reason: g.gitSkipDetail });
|
|
1825
|
+
steps.push({ step: 'verify_merge', status: 'skipped', reason: g.gitSkipDetail });
|
|
1826
|
+
} else if (g.gitSkipped) {
|
|
1827
|
+
steps.push({ step: 'git', status: 'skipped', reason: g.gitSkipReason || 'git step skipped' });
|
|
1828
|
+
} else if (g.noGitRepo) {
|
|
1829
|
+
steps.push({ step: 'git', status: 'skipped', reason: 'not a git repository' });
|
|
1830
|
+
} else if (g.noChanges) {
|
|
1831
|
+
steps.push({ step: 'git', status: 'skipped', reason: g.memoryOnly ? 'memory-only task' : 'no changes within declared scope' });
|
|
1832
|
+
} else {
|
|
1833
|
+
gitFailed = true;
|
|
1834
|
+
const cause = [classifyMergeFailureReason(g), g.blockedReason, g.mergeErrorText, g.gitException, g.verify_merge_landed_reason]
|
|
1835
|
+
.filter(Boolean).map(s => String(s).split('\n')[0]).filter((s, i, a) => a.indexOf(s) === i).join(' | ');
|
|
1836
|
+
steps.push({ step: 'git', status: 'failed', reason: cause });
|
|
1837
|
+
}
|
|
1838
|
+
let cardState = null;
|
|
1839
|
+
if (gitFailed) {
|
|
1840
|
+
if (result.taskBlocked || result.reconciliationApplied) {
|
|
1841
|
+
cardState = 'blocked';
|
|
1842
|
+
steps.push({ step: 'move_blocked', status: 'ok' });
|
|
1843
|
+
} else if (result.reconciliationChecked) {
|
|
1844
|
+
cardState = 'active';
|
|
1845
|
+
steps.push({ step: 'move_blocked', status: 'failed', reason: g.blockError || 'card is not in done/ but could not be moved to blocked/' });
|
|
1846
|
+
} else {
|
|
1847
|
+
cardState = 'done_unreconciled';
|
|
1848
|
+
steps.push({ step: 'move_blocked', status: 'failed', reason: g.blockError || 'unknown' });
|
|
1849
|
+
steps.push({ step: 'reconcile', status: 'failed', reason: result.reconcileError || 'reconciliation did not run' });
|
|
1850
|
+
}
|
|
1851
|
+
} else if (g && !result.taskCompleted && result.completeError) {
|
|
1852
|
+
warnings.push(`card completion step failed: ${result.completeError || 'see log'}`);
|
|
1853
|
+
}
|
|
1854
|
+
if (result.forsetiL0 && result.forsetiL0.status === 'FAIL') {
|
|
1855
|
+
warnings.push(`Forseti L0: ${result.forsetiL0.failures.map(f => f.message).join('; ')}`);
|
|
1856
|
+
}
|
|
1857
|
+
const outcome = gitFailed ? 'not_completed' : (warnings.length ? 'completed_with_warnings' : 'completed');
|
|
1858
|
+
return { outcome, cardState, steps, warnings };
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1781
1861
|
function blockTaskForMergeFailure(taskId, projectPath, gitResult) {
|
|
1782
1862
|
if (!taskId) return false;
|
|
1783
1863
|
|
|
@@ -1858,7 +1938,9 @@ function blockTaskForMergeFailure(taskId, projectPath, gitResult) {
|
|
|
1858
1938
|
|
|
1859
1939
|
if (updateResult.status !== 0) {
|
|
1860
1940
|
const errMsg = (updateResult.stderr || updateResult.stdout || '').trim();
|
|
1861
|
-
|
|
1941
|
+
const cause = spawnFailureCause(updateResult);
|
|
1942
|
+
console.log(` โ ๏ธ Failed to move ${taskId} to blocked/: ${cause}`);
|
|
1943
|
+
if (gitResult) gitResult.blockError = cause;
|
|
1862
1944
|
return false;
|
|
1863
1945
|
}
|
|
1864
1946
|
|
|
@@ -2039,23 +2121,6 @@ setStagingConfig(CONFIG);
|
|
|
2039
2121
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
2040
2122
|
|
|
2041
2123
|
const TASK_STATUS_DIRS = ['backlog', 'todo', 'active', 'done', 'blocked'];
|
|
2042
|
-
// ?????????????????????????????????????????????????????????????????????????????
|
|
2043
|
-
// SECTION: HEALTH_AND_GUARDIAN (extracted to ./post-complete/health.js)
|
|
2044
|
-
// ?????????????????????????????????????????????????????????????????????????????
|
|
2045
|
-
const {
|
|
2046
|
-
resolveMcpProjectPath,
|
|
2047
|
-
readLoopConfig,
|
|
2048
|
-
measureHealthScoreSync,
|
|
2049
|
-
readPreTaskHealthSnapshot,
|
|
2050
|
-
saveTaskHealthSnapshot,
|
|
2051
|
-
computeHealthVerdict,
|
|
2052
|
-
writeVerdictToTask,
|
|
2053
|
-
readGuardianCounter,
|
|
2054
|
-
writeGuardianCounter,
|
|
2055
|
-
triggerGuardianHealthCheck,
|
|
2056
|
-
HEALTH_SNAPSHOTS_DIR_NAME,
|
|
2057
|
-
} = require('./post-complete/health');
|
|
2058
|
-
|
|
2059
2124
|
// TASK_1885: deliberately NOT the same knob as loopCfg.revert_threshold (DEFAULT_REVERT_THRESHOLD
|
|
2060
2125
|
// in health.js). This is a fixed calibration point for the Design Reasoning Attribution valence
|
|
2061
2126
|
// classifier (feeds skill matrix / EFE learning signals) -- it answers "was this outcome bad
|
|
@@ -2076,8 +2141,6 @@ const {
|
|
|
2076
2141
|
parseCognitiveCheckpoint,
|
|
2077
2142
|
detectCognitiveCheckpoint,
|
|
2078
2143
|
collectThinkingTags,
|
|
2079
|
-
triggerPassiveRuneExtraction,
|
|
2080
|
-
callHippocampusMcpSync,
|
|
2081
2144
|
} = require('./post-complete/cognitive');
|
|
2082
2145
|
initCognitive({ findLogForTask, getPostCompleteChangedFiles, rankTouchedFiles, stripCJK });
|
|
2083
2146
|
|
|
@@ -2094,7 +2157,7 @@ const {
|
|
|
2094
2157
|
updateSkillMatrix,
|
|
2095
2158
|
accumulateValence,
|
|
2096
2159
|
} = require('./post-complete/outcome');
|
|
2097
|
-
initOutcome({ resolveProjectRoot, resolveTasksBase, loadTaskScopePaths, getPostCompleteChangedFiles
|
|
2160
|
+
initOutcome({ resolveProjectRoot, resolveTasksBase, loadTaskScopePaths, getPostCompleteChangedFiles });
|
|
2098
2161
|
|
|
2099
2162
|
// ?????????????????????????????????????????????????????????????????????????????
|
|
2100
2163
|
// SECTION: INTEGRATION_GAPS (extracted to ./post-complete/integration-gaps.js)
|
|
@@ -2155,6 +2218,7 @@ function postComplete(opts = {}) {
|
|
|
2155
2218
|
formation,
|
|
2156
2219
|
skill,
|
|
2157
2220
|
worktreePath = process.env.FORMATION_WORKTREE_PATH || null,
|
|
2221
|
+
noSource = false,
|
|
2158
2222
|
} = opts;
|
|
2159
2223
|
|
|
2160
2224
|
// B11: opportunistic GC of orphan worktrees from prior failed dispatches
|
|
@@ -2182,56 +2246,6 @@ function postComplete(opts = {}) {
|
|
|
2182
2246
|
|
|
2183
2247
|
const result = { archive: null, git: null };
|
|
2184
2248
|
|
|
2185
|
-
// Step 0: Hippocampus Episode Start (episodic memory โ TASK_716)
|
|
2186
|
-
// TASK_963: Resolve touched file_paths so Rust can create TOUCHES edges to Symbols
|
|
2187
|
-
let hippocampusEpisodeId = null;
|
|
2188
|
-
if (taskId && !dryRun) {
|
|
2189
|
-
try {
|
|
2190
|
-
// Collect file_paths from worktree git diff or task scope_paths
|
|
2191
|
-
let filePaths = [];
|
|
2192
|
-
const diffRoot = worktreePath || resolveProjectRoot(projectPath);
|
|
2193
|
-
if (diffRoot) {
|
|
2194
|
-
try {
|
|
2195
|
-
const diffCmd = worktreePath
|
|
2196
|
-
? 'git diff --name-only main...HEAD'
|
|
2197
|
-
: 'git diff --name-only HEAD~1 HEAD';
|
|
2198
|
-
const diffOut = spawnSync('git', diffCmd.split(' ').slice(1), {
|
|
2199
|
-
cwd: diffRoot, encoding: 'utf8', timeout: 8000, windowsHide: true,
|
|
2200
|
-
});
|
|
2201
|
-
if (diffOut.status === 0 && diffOut.stdout) {
|
|
2202
|
-
filePaths = diffOut.stdout.trim().split('\n').filter(Boolean);
|
|
2203
|
-
}
|
|
2204
|
-
} catch { /* git diff failed โ fall through to scope_paths */ }
|
|
2205
|
-
}
|
|
2206
|
-
if (filePaths.length === 0) {
|
|
2207
|
-
filePaths = loadTaskScopePaths(taskId, projectPath);
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
const epArgs = {
|
|
2211
|
-
task_id: taskId,
|
|
2212
|
-
scope: `post-complete:${taskId}`,
|
|
2213
|
-
agent_role: formation || 'unit',
|
|
2214
|
-
agent_skill: skill || 'do',
|
|
2215
|
-
...(filePaths.length > 0 ? { file_paths: filePaths } : {}),
|
|
2216
|
-
// TASK_2025: hippocampus_episode_start is now a write tool that requires
|
|
2217
|
-
// an explicit projectPath (mcp.rs resolve_project_for_write) -- it no
|
|
2218
|
-
// longer silently falls back to the Dashboard's currently-selected
|
|
2219
|
-
// project. projectPath is only set here when --project-path was passed
|
|
2220
|
-
// (sub-project dispatch); core-project dispatches never pass it, so
|
|
2221
|
-
// fall back to ROOT (this script's own core project root) rather than
|
|
2222
|
-
// omitting the param and hard-failing every core-project run.
|
|
2223
|
-
projectPath: projectPath || ROOT,
|
|
2224
|
-
};
|
|
2225
|
-
const epResult = callHippocampusMcpSync('hippocampus_episode_start', epArgs);
|
|
2226
|
-
if (epResult && epResult.episode_id) {
|
|
2227
|
-
hippocampusEpisodeId = epResult.episode_id;
|
|
2228
|
-
console.log(` ๐ง Hippocampus: episode ${hippocampusEpisodeId} started (${filePaths.length} file_paths)`);
|
|
2229
|
-
}
|
|
2230
|
-
} catch (e) {
|
|
2231
|
-
console.log(` โ ๏ธ Hippocampus episode start skipped: ${e.message}`);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
|
|
2235
2249
|
// Step 1: Archive
|
|
2236
2250
|
if (!skipArchive) {
|
|
2237
2251
|
console.log('โโ Archive Check โโ');
|
|
@@ -2325,7 +2339,13 @@ function postComplete(opts = {}) {
|
|
|
2325
2339
|
// branch commits + the YAML move enter main).
|
|
2326
2340
|
// Flag absent (default) => legacy gitBranchAndMerge path, byte-identical
|
|
2327
2341
|
// to previous behavior โ rollback is trivial. Dry runs stay on legacy.
|
|
2328
|
-
if (
|
|
2342
|
+
if (noSource) {
|
|
2343
|
+
// TASK_2420: explicit source-less completion (runtime/config repair, no branch).
|
|
2344
|
+
gitResult = buildNoSourceGitResult(taskId, projectPath, worktreePath);
|
|
2345
|
+
console.log(gitResult.noSource
|
|
2346
|
+
? ` โญ๏ธ git merge/verify skipped (${gitResult.gitSkipDetail})`
|
|
2347
|
+
: ` โ --no-source refused: ${gitResult.blockedReason}`);
|
|
2348
|
+
} else if (process.env.KENAZ_INTEGRATION_QUEUE === '1' && !dryRun) {
|
|
2329
2349
|
gitResult = require('./integration.js').integrateForPostComplete({ taskId, projectPath, message });
|
|
2330
2350
|
} else {
|
|
2331
2351
|
gitResult = gitBranchAndMerge({ taskId, message, dryRun, projectPath, skipTests, testCommands, worktreePath });
|
|
@@ -2356,17 +2376,6 @@ function postComplete(opts = {}) {
|
|
|
2356
2376
|
} catch { /* commit-sync not available โ graceful degradation */ }
|
|
2357
2377
|
}
|
|
2358
2378
|
|
|
2359
|
-
// Step 2.6: Passive Rune Extraction๏ผfire-and-forget๏ผ
|
|
2360
|
-
if (gitResult.committed && !dryRun) {
|
|
2361
|
-
try {
|
|
2362
|
-
const commitMsg = message || `[${taskId}] task completed`;
|
|
2363
|
-
const projectRoot = resolveProjectRoot(projectPath);
|
|
2364
|
-
if (projectRoot) {
|
|
2365
|
-
triggerPassiveRuneExtraction(commitMsg, projectRoot, projectPath);
|
|
2366
|
-
}
|
|
2367
|
-
} catch { /* rune extraction failed โ never block post-complete */ }
|
|
2368
|
-
}
|
|
2369
|
-
|
|
2370
2379
|
// Step 2.7: Outcome Recording + Surprise Detection๏ผfire-and-forget๏ผ
|
|
2371
2380
|
if (!dryRun) {
|
|
2372
2381
|
try {
|
|
@@ -2472,10 +2481,12 @@ function postComplete(opts = {}) {
|
|
|
2472
2481
|
result.taskCompleted = true;
|
|
2473
2482
|
console.log(` โ
Task ${taskId} already in done/`);
|
|
2474
2483
|
} else {
|
|
2475
|
-
|
|
2484
|
+
result.completeError = spawnFailureCause(completeResult);
|
|
2485
|
+
console.log(` โ ๏ธ Failed to complete task: ${result.completeError}`);
|
|
2476
2486
|
}
|
|
2477
2487
|
}
|
|
2478
2488
|
} catch (e) {
|
|
2489
|
+
result.completeError = e.message;
|
|
2479
2490
|
console.log(` โ ๏ธ Task complete error: ${e.message}`);
|
|
2480
2491
|
}
|
|
2481
2492
|
} else if (taskId && !dryRun && result.git && result.git.noChanges) {
|
|
@@ -2503,10 +2514,12 @@ function postComplete(opts = {}) {
|
|
|
2503
2514
|
result.taskCompleted = true;
|
|
2504
2515
|
console.log(` โ
Task ${taskId} already in done/`);
|
|
2505
2516
|
} else {
|
|
2506
|
-
|
|
2517
|
+
result.completeError = spawnFailureCause(completeResult);
|
|
2518
|
+
console.log(` โ ๏ธ Failed to complete task: ${result.completeError}`);
|
|
2507
2519
|
}
|
|
2508
2520
|
}
|
|
2509
2521
|
} catch (e) {
|
|
2522
|
+
result.completeError = e.message;
|
|
2510
2523
|
console.log(` โ ๏ธ Task complete error: ${e.message}`);
|
|
2511
2524
|
}
|
|
2512
2525
|
} else if (taskId && result.git && !isVerifiedMergeSuccess(result.git) && !result.git.noGitRepo && !result.git.noChanges) {
|
|
@@ -2599,12 +2612,14 @@ function postComplete(opts = {}) {
|
|
|
2599
2612
|
console.log(` ๐จ [TASK_1536] Reconciliation: task ${taskId} was in done/ but merge did not land โ moved to blocked/ (failure_reason=${reconcileVitals.failure_reason})`);
|
|
2600
2613
|
} else if (reconcileResult.status === 0) {
|
|
2601
2614
|
// task was not in done/ โ already blocked or active, nothing to reconcile
|
|
2615
|
+
result.reconciliationChecked = true;
|
|
2602
2616
|
console.log(` โ
[TASK_1536] Reconciliation check: task ${taskId} not in done/ โ no reconciliation needed`);
|
|
2603
2617
|
} else {
|
|
2604
|
-
|
|
2605
|
-
console.log(` โ ๏ธ [TASK_1536] Reconciliation failed: ${
|
|
2618
|
+
result.reconcileError = spawnFailureCause(reconcileResult);
|
|
2619
|
+
console.log(` โ ๏ธ [TASK_1536] Reconciliation failed: ${result.reconcileError}`);
|
|
2606
2620
|
}
|
|
2607
2621
|
} catch (reconcileErr) {
|
|
2622
|
+
result.reconcileError = reconcileErr.message || String(reconcileErr);
|
|
2608
2623
|
console.log(` โ ๏ธ [TASK_1536] Reconciliation error (non-fatal): ${(reconcileErr.message || '').split('\n')[0]}`);
|
|
2609
2624
|
}
|
|
2610
2625
|
}
|
|
@@ -2636,10 +2651,12 @@ function postComplete(opts = {}) {
|
|
|
2636
2651
|
result.taskCompleted = true;
|
|
2637
2652
|
console.log(` โ
Task ${taskId} already in done/`);
|
|
2638
2653
|
} else {
|
|
2639
|
-
|
|
2654
|
+
result.completeError = spawnFailureCause(moveResult);
|
|
2655
|
+
console.log(` โ ๏ธ Safety net move failed: ${result.completeError}`);
|
|
2640
2656
|
}
|
|
2641
2657
|
}
|
|
2642
2658
|
} catch (e) {
|
|
2659
|
+
result.completeError = e.message;
|
|
2643
2660
|
console.log(` โ ๏ธ Safety net error: ${e.message}`);
|
|
2644
2661
|
}
|
|
2645
2662
|
}
|
|
@@ -2694,74 +2711,6 @@ function postComplete(opts = {}) {
|
|
|
2694
2711
|
}
|
|
2695
2712
|
}
|
|
2696
2713
|
|
|
2697
|
-
// Step 2.5: Passive Rune Extraction โ task completion signal (TASK_747)
|
|
2698
|
-
if (result.taskCompleted && taskId && !dryRun) {
|
|
2699
|
-
try {
|
|
2700
|
-
const runeScope = loadTaskScopePaths(taskId, projectPath);
|
|
2701
|
-
if (runeScope.length > 0) {
|
|
2702
|
-
// Read task title and requirements via task-api get
|
|
2703
|
-
let runeContent = `Task ${taskId}: completed.`;
|
|
2704
|
-
try {
|
|
2705
|
-
const getArgs = projectPath ? ['--project-path', projectPath] : [];
|
|
2706
|
-
getArgs.push('get', taskId);
|
|
2707
|
-
const getResult = spawnSync('node', [COLLABORATION_API, ...getArgs], {
|
|
2708
|
-
encoding: 'utf8', timeout: 10000, cwd: ROOT, windowsHide: true,
|
|
2709
|
-
});
|
|
2710
|
-
if (getResult.status === 0 && getResult.stdout) {
|
|
2711
|
-
const taskData = JSON.parse(getResult.stdout.trim());
|
|
2712
|
-
const rawTitle = taskData.title || '';
|
|
2713
|
-
const reqs = Array.isArray(taskData.requirements) ? taskData.requirements : [];
|
|
2714
|
-
// MEM-LANG-001: strip CJK so FTS5 unicode61 tokenizer can index the rune
|
|
2715
|
-
const title = stripCJK(rawTitle);
|
|
2716
|
-
const reqSummary = stripCJK(reqs.slice(0, 3).join('; '));
|
|
2717
|
-
// Fallback: if English portion of title is too short, use task ID as title
|
|
2718
|
-
const effectiveTitle = title.length >= 10 ? title : `${taskId} task`;
|
|
2719
|
-
runeContent = `Task ${taskId}: ${effectiveTitle}. Requirements: ${reqSummary}`;
|
|
2720
|
-
if (runeContent.length > 300) runeContent = runeContent.slice(0, 297) + '...';
|
|
2721
|
-
}
|
|
2722
|
-
} catch { /* skip โ use fallback content */ }
|
|
2723
|
-
|
|
2724
|
-
// Fire-and-forget synapse_inscribe for each scope path
|
|
2725
|
-
const http = require('http');
|
|
2726
|
-
let fired = 0;
|
|
2727
|
-
for (const scopePath of runeScope) {
|
|
2728
|
-
try {
|
|
2729
|
-
const inscribeArgs = {
|
|
2730
|
-
symbol_id: scopePath,
|
|
2731
|
-
type: 'summary',
|
|
2732
|
-
content: runeContent,
|
|
2733
|
-
confidence: 0.65,
|
|
2734
|
-
linked_task: taskId,
|
|
2735
|
-
// TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
|
|
2736
|
-
projectPath: projectPath || ROOT,
|
|
2737
|
-
};
|
|
2738
|
-
const payload = JSON.stringify({
|
|
2739
|
-
jsonrpc: '2.0',
|
|
2740
|
-
id: `task-rune-${taskId}-${fired}`,
|
|
2741
|
-
method: 'tools/call',
|
|
2742
|
-
params: { name: 'synapse_inscribe', arguments: inscribeArgs },
|
|
2743
|
-
});
|
|
2744
|
-
const req = http.request({
|
|
2745
|
-
hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
|
|
2746
|
-
method: 'POST',
|
|
2747
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
2748
|
-
timeout: 10000,
|
|
2749
|
-
}, (res) => {
|
|
2750
|
-
res.on('data', () => {});
|
|
2751
|
-
res.on('end', () => {});
|
|
2752
|
-
});
|
|
2753
|
-
req.on('error', (err) => { console.warn(` โ ๏ธ synapse_inscribe HTTP error: ${err.message}`); });
|
|
2754
|
-
req.on('timeout', () => { req.destroy(); });
|
|
2755
|
-
req.write(payload);
|
|
2756
|
-
req.end();
|
|
2757
|
-
fired++;
|
|
2758
|
-
} catch { /* skip this scope path */ }
|
|
2759
|
-
}
|
|
2760
|
-
console.log(` ๐ง Rune extraction: ${fired} scope symbols for ${taskId}`);
|
|
2761
|
-
}
|
|
2762
|
-
} catch { /* skip โ never blocks post-complete */ }
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
2714
|
// Step 2.55 (cognitive tool usage -> cognitive_competence) was removed in TASK_2348: its only
|
|
2766
2715
|
// data source, turn_feedback.jsonl, was written by the retired reflex hook.
|
|
2767
2716
|
|
|
@@ -3077,138 +3026,6 @@ function postComplete(opts = {}) {
|
|
|
3077
3026
|
}
|
|
3078
3027
|
}
|
|
3079
3028
|
|
|
3080
|
-
// Step 3b: Provenance rune โ record which task + role modified scope paths (Cognee-inspired)
|
|
3081
|
-
if (taskId && !dryRun && result.git && result.git.merged) {
|
|
3082
|
-
try {
|
|
3083
|
-
const scopePaths = loadTaskScopePaths(taskId, projectPath);
|
|
3084
|
-
const role = result.taskRole || 'unknown';
|
|
3085
|
-
if (scopePaths.length > 0 && scopePaths.length <= 5) {
|
|
3086
|
-
const http = require('http');
|
|
3087
|
-
for (const scopePath of scopePaths) {
|
|
3088
|
-
try {
|
|
3089
|
-
const payload = JSON.stringify({
|
|
3090
|
-
jsonrpc: '2.0', id: `provenance-${taskId}`,
|
|
3091
|
-
method: 'tools/call',
|
|
3092
|
-
params: { name: 'synapse_inscribe', arguments: {
|
|
3093
|
-
symbol_id: scopePath,
|
|
3094
|
-
type: 'summary',
|
|
3095
|
-
content: `Modified by ${taskId} (${role}). Scope: ${scopePaths.join(', ')}`,
|
|
3096
|
-
confidence: 0.6,
|
|
3097
|
-
author: `agent:${role}`,
|
|
3098
|
-
linked_task: taskId,
|
|
3099
|
-
// TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
|
|
3100
|
-
projectPath: projectPath || ROOT,
|
|
3101
|
-
}},
|
|
3102
|
-
});
|
|
3103
|
-
const req = http.request({
|
|
3104
|
-
hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
|
|
3105
|
-
method: 'POST',
|
|
3106
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
3107
|
-
timeout: 5000,
|
|
3108
|
-
}, (res) => { res.on('data', () => {}); res.on('end', () => {}); });
|
|
3109
|
-
req.on('error', () => {});
|
|
3110
|
-
req.on('timeout', () => { req.destroy(); });
|
|
3111
|
-
req.write(payload);
|
|
3112
|
-
req.end();
|
|
3113
|
-
} catch { /* skip */ }
|
|
3114
|
-
}
|
|
3115
|
-
console.log(` ๐ Provenance runes: ${scopePaths.length} scope paths tagged with ${taskId}/${role}`);
|
|
3116
|
-
}
|
|
3117
|
-
} catch { /* never blocks */ }
|
|
3118
|
-
}
|
|
3119
|
-
|
|
3120
|
-
// Step 4: Guardian Health Check + KEEP/REVERT delta (TASK_706)
|
|
3121
|
-
if (taskId) {
|
|
3122
|
-
const counter = readGuardianCounter();
|
|
3123
|
-
counter.completed_since_last_health = (counter.completed_since_last_health || 0) + 1;
|
|
3124
|
-
|
|
3125
|
-
const loopCfg = readLoopConfig(projectPath);
|
|
3126
|
-
const regressionSkipVerdictReason = result.git?.regression?.skipVerdictReason || '';
|
|
3127
|
-
const shouldSkipHealthVerdict = !!result.git?.regression?.skipVerdict;
|
|
3128
|
-
|
|
3129
|
-
// TASK_2341: synapse_health now scores MEMORY (rune/episode/conflict backlog), not code,
|
|
3130
|
-
// so it can never revert, block or penalise a task. Both modes only RECORD the trend.
|
|
3131
|
-
if (loopCfg.mode === 'auto' && shouldSkipHealthVerdict) {
|
|
3132
|
-
console.log('โโ Guardian Memory-Health Trend (loop auto mode) โโ');
|
|
3133
|
-
console.log(` โ ๏ธ Guardian: health snapshot skipped โ ${regressionSkipVerdictReason}`);
|
|
3134
|
-
result.healthVerdictSkipped = { reason: regressionSkipVerdictReason };
|
|
3135
|
-
counter.completed_since_last_health = 0;
|
|
3136
|
-
counter.last_health_check = new Date().toISOString();
|
|
3137
|
-
writeGuardianCounter(counter);
|
|
3138
|
-
} else {
|
|
3139
|
-
const preSn = readPreTaskHealthSnapshot(taskId, projectPath);
|
|
3140
|
-
if (preSn && typeof preSn.score === 'number') {
|
|
3141
|
-
console.log(`โโ Guardian Memory-Health Trend (${loopCfg.mode} mode โ record only, never reverts) โโ`);
|
|
3142
|
-
const postHealth = measureHealthScoreSync(projectPath);
|
|
3143
|
-
if (postHealth) {
|
|
3144
|
-
const afterPayload = saveTaskHealthSnapshot(taskId, 'after', postHealth, projectPath);
|
|
3145
|
-
const vr = computeHealthVerdict(preSn, postHealth, loopCfg.revert_threshold, {
|
|
3146
|
-
trendDeltaFromPrevious: afterPayload ? afterPayload.delta_from_previous : null,
|
|
3147
|
-
});
|
|
3148
|
-
console.log(` ๐ก๏ธ Memory health: ${postHealth.score}/100 (${vr.reason})`);
|
|
3149
|
-
if (typeof vr.trendDeltaFromPrevious === 'number') {
|
|
3150
|
-
console.log(` ๐ Trend (delta_from_previous): ${vr.trendDeltaFromPrevious >= 0 ? '+' : ''}${vr.trendDeltaFromPrevious}`);
|
|
3151
|
-
}
|
|
3152
|
-
writeVerdictToTask(taskId, vr.verdict, vr.delta, vr.reason, projectPath);
|
|
3153
|
-
result.healthVerdict = { verdict: vr.verdict, delta: vr.delta, reason: vr.reason };
|
|
3154
|
-
// Auto-acted_on: mark runes recalled during this task as acted_on (closes the
|
|
3155
|
-
// recall->acted_on->confidence_boost loop). Route still exists (synapse.rs).
|
|
3156
|
-
if (loopCfg.mode === 'auto') {
|
|
3157
|
-
try {
|
|
3158
|
-
const actedOnPayload = JSON.stringify({ taskId });
|
|
3159
|
-
const actReq = require('http').request({
|
|
3160
|
-
hostname: '127.0.0.1',
|
|
3161
|
-
port: 18080,
|
|
3162
|
-
path: '/api/synapse/runes/mark-acted-on-by-task',
|
|
3163
|
-
method: 'POST',
|
|
3164
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(actedOnPayload) },
|
|
3165
|
-
timeout: 3000,
|
|
3166
|
-
}, (res) => {
|
|
3167
|
-
let body = '';
|
|
3168
|
-
res.on('data', c => body += c);
|
|
3169
|
-
res.on('end', () => {
|
|
3170
|
-
try {
|
|
3171
|
-
const r = JSON.parse(body);
|
|
3172
|
-
if (r.marked > 0) console.log(` ๐ Acted-on: ${r.marked} runes marked for ${taskId}`);
|
|
3173
|
-
} catch { /* parse error โ silent */ }
|
|
3174
|
-
});
|
|
3175
|
-
});
|
|
3176
|
-
actReq.on('error', () => { /* Dashboard offline โ silent skip */ });
|
|
3177
|
-
actReq.write(actedOnPayload);
|
|
3178
|
-
actReq.end();
|
|
3179
|
-
} catch { /* never blocks post-complete */ }
|
|
3180
|
-
}
|
|
3181
|
-
counter.completed_since_last_health = 0;
|
|
3182
|
-
counter.last_health_check = new Date().toISOString();
|
|
3183
|
-
} else {
|
|
3184
|
-
console.log(` ๐ก๏ธ Guardian: Dashboard not running โ after-snapshot skipped for ${taskId}`);
|
|
3185
|
-
}
|
|
3186
|
-
} else if (loopCfg.mode === 'auto') {
|
|
3187
|
-
const postHealth = measureHealthScoreSync(projectPath);
|
|
3188
|
-
if (postHealth) {
|
|
3189
|
-
saveTaskHealthSnapshot(taskId, 'before', postHealth, projectPath);
|
|
3190
|
-
console.log(` ๐ก๏ธ Guardian: memory health=${postHealth.score}/100 (${postHealth.grade}) โ saved as baseline (no pre-task snapshot found)`);
|
|
3191
|
-
} else {
|
|
3192
|
-
console.log(' ๐ก๏ธ Guardian: Dashboard not running โ health snapshot skipped');
|
|
3193
|
-
}
|
|
3194
|
-
counter.completed_since_last_health = 0;
|
|
3195
|
-
counter.last_health_check = new Date().toISOString();
|
|
3196
|
-
} else if (counter.completed_since_last_health >= CONFIG.GUARDIAN_INTERVAL) {
|
|
3197
|
-
console.log(`โโ Guardian Health Check (every ${CONFIG.GUARDIAN_INTERVAL} tasks) โโ`);
|
|
3198
|
-
triggerGuardianHealthCheck(projectPath);
|
|
3199
|
-
counter.completed_since_last_health = 0;
|
|
3200
|
-
counter.last_health_check = new Date().toISOString();
|
|
3201
|
-
} else {
|
|
3202
|
-
const remaining = CONFIG.GUARDIAN_INTERVAL - counter.completed_since_last_health;
|
|
3203
|
-
console.log(` ๐ก๏ธ Guardian: ${remaining} tasks until next health snapshot (no pre-dispatch snapshot for ${taskId})`);
|
|
3204
|
-
}
|
|
3205
|
-
}
|
|
3206
|
-
|
|
3207
|
-
if (!shouldSkipHealthVerdict || loopCfg.mode !== 'auto') {
|
|
3208
|
-
writeGuardianCounter(counter);
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
3029
|
// Step 4.6: Guard Failure Detector (TASK_1653)
|
|
3213
3030
|
// Runs shadow-mode failure detectors for active lesson guards that carry failure_signature.
|
|
3214
3031
|
// Reads guard snapshots directly from lesson_guards.yaml (not the task chart) because
|
|
@@ -3376,14 +3193,6 @@ function postComplete(opts = {}) {
|
|
|
3376
3193
|
console.log(` ๐ On branch: ${result.git.userBranch} โ merge to main when ready`);
|
|
3377
3194
|
}
|
|
3378
3195
|
}
|
|
3379
|
-
// Health verdict summary (TASK_706)
|
|
3380
|
-
if (result.healthVerdict) {
|
|
3381
|
-
const hv = result.healthVerdict;
|
|
3382
|
-
console.log(` Memory Health: ${hv.verdict} (trend=${typeof hv.delta === 'number' ? hv.delta.toFixed(1) : 'n/a'}) โ ${hv.reason}`);
|
|
3383
|
-
} else if (result.healthVerdictSkipped) {
|
|
3384
|
-
console.log(` Health Verdict: SKIPPED โ ๏ธ โ ${result.healthVerdictSkipped.reason}`);
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
3196
|
// Cognitive checkpoint summary
|
|
3388
3197
|
if (result.cognitive) {
|
|
3389
3198
|
const cog = result.cognitive;
|
|
@@ -3398,84 +3207,6 @@ function postComplete(opts = {}) {
|
|
|
3398
3207
|
}
|
|
3399
3208
|
console.log('');
|
|
3400
3209
|
|
|
3401
|
-
// Step 8a: Hippocampus Episode Append โ record task outcome (TASK_969)
|
|
3402
|
-
if (hippocampusEpisodeId && !dryRun) {
|
|
3403
|
-
try {
|
|
3404
|
-
const contentParts = [`Task ${taskId} outcome:`];
|
|
3405
|
-
|
|
3406
|
-
// Changed files
|
|
3407
|
-
const changedFileList = result.git?.diffStat || [];
|
|
3408
|
-
if (changedFileList.length > 0) {
|
|
3409
|
-
contentParts.push(`Changed files: ${changedFileList.join(', ')}`);
|
|
3410
|
-
} else if (result.git?.committed) {
|
|
3411
|
-
contentParts.push('Changes committed (file list unavailable).');
|
|
3412
|
-
} else if (result.git?.noChanges) {
|
|
3413
|
-
contentParts.push('No file changes detected.');
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
// Health delta (regression test results)
|
|
3417
|
-
if (result.git?.regression?.skipVerdict) {
|
|
3418
|
-
contentParts.push(`Health verdict skipped: ${result.git.regression.skipVerdictReason}.`);
|
|
3419
|
-
} else if (result.git?.regression && !result.git.regression.skipped) {
|
|
3420
|
-
contentParts.push(result.git.regression.passed
|
|
3421
|
-
? 'Health: tests passed.'
|
|
3422
|
-
: `Health: tests FAILED.${result.git.regressionFixTask ? ` Fix task: ${result.git.regressionFixTask}` : ''}`);
|
|
3423
|
-
}
|
|
3424
|
-
|
|
3425
|
-
// Fix loop status
|
|
3426
|
-
if (result.git?.acceptanceFixTasks && result.git.acceptanceFixTasks.length > 0) {
|
|
3427
|
-
contentParts.push(`Fix loop triggered: ${result.git.acceptanceFixTasks.join(', ')}`);
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
|
-
// Git merge status
|
|
3431
|
-
if (isVerifiedMergeSuccess(result.git)) {
|
|
3432
|
-
contentParts.push(`Merged to ${result.git.userBranch}.`);
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
|
-
const appendArgs = {
|
|
3436
|
-
episode_id: hippocampusEpisodeId,
|
|
3437
|
-
phase: 'outcome',
|
|
3438
|
-
content: contentParts.join(' '),
|
|
3439
|
-
confidence: 0.8,
|
|
3440
|
-
outcome: isVerifiedMergeSuccess(result.git) ? 'success' : 'partial',
|
|
3441
|
-
// TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
|
|
3442
|
-
projectPath: projectPath || ROOT,
|
|
3443
|
-
};
|
|
3444
|
-
callHippocampusMcpSync('hippocampus_episode_append', appendArgs);
|
|
3445
|
-
console.log(` ๐ง Hippocampus: episode ${hippocampusEpisodeId} appended outcome entry`);
|
|
3446
|
-
} catch (e) {
|
|
3447
|
-
console.log(` โ ๏ธ Hippocampus episode append skipped: ${e.message}`);
|
|
3448
|
-
}
|
|
3449
|
-
}
|
|
3450
|
-
|
|
3451
|
-
// Step 8b: Hippocampus Episode Close (episodic memory โ TASK_716)
|
|
3452
|
-
if (hippocampusEpisodeId && !dryRun) {
|
|
3453
|
-
try {
|
|
3454
|
-
const outcome = isVerifiedMergeSuccess(result.git)
|
|
3455
|
-
? 'success'
|
|
3456
|
-
: (result.git?.noChanges || result.git?.memoryOnly ? 'success' : 'partial');
|
|
3457
|
-
const summaryParts = [`Task ${taskId} post-complete finished.`];
|
|
3458
|
-
if (isVerifiedMergeSuccess(result.git)) summaryParts.push(`Git merged to ${result.git.userBranch}.`);
|
|
3459
|
-
if (result.taskCompleted) summaryParts.push('Task marked done.');
|
|
3460
|
-
if (result.git?.regression?.skipVerdict) {
|
|
3461
|
-
summaryParts.push('Health verdict skipped due to cargo lock contention.');
|
|
3462
|
-
} else if (result.git?.regression && !result.git.regression.skipped) {
|
|
3463
|
-
summaryParts.push(result.git.regression.passed ? 'Tests passed.' : 'Tests failed โ fix task created.');
|
|
3464
|
-
}
|
|
3465
|
-
const closeArgs = {
|
|
3466
|
-
episode_id: hippocampusEpisodeId,
|
|
3467
|
-
outcome,
|
|
3468
|
-
summary: summaryParts.join(' '),
|
|
3469
|
-
// TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
|
|
3470
|
-
projectPath: projectPath || ROOT,
|
|
3471
|
-
};
|
|
3472
|
-
callHippocampusMcpSync('hippocampus_episode_close', closeArgs);
|
|
3473
|
-
console.log(` ๐ง Hippocampus: episode ${hippocampusEpisodeId} closed (${outcome})`);
|
|
3474
|
-
} catch (e) {
|
|
3475
|
-
console.log(` โ ๏ธ Hippocampus episode close skipped: ${e.message}`);
|
|
3476
|
-
}
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3479
3210
|
// TASK_1593: Record outcome in dedup guard so a failed pass does not
|
|
3480
3211
|
// suppress the next legitimate retry (Guard 1 worktree->null check).
|
|
3481
3212
|
if (taskId && !dryRun) {
|
|
@@ -3484,6 +3215,14 @@ function postComplete(opts = {}) {
|
|
|
3484
3215
|
updatePostCompleteGuardOutcome(guardFile, taskId, passSucceeded ? 'success' : 'failure');
|
|
3485
3216
|
}
|
|
3486
3217
|
|
|
3218
|
+
// TASK_2420: truthful outcome โ never "completed" when a required step failed.
|
|
3219
|
+
result.summary = summarizePostComplete(result);
|
|
3220
|
+
if (taskId && !dryRun && result.summary.outcome !== 'completed') {
|
|
3221
|
+
console.log(`โโ Outcome: ${result.summary.outcome.toUpperCase()} โโ`);
|
|
3222
|
+
for (const st of result.summary.steps) console.log(` ${st.status === 'failed' ? 'โ' : st.status === 'skipped' ? 'โญ๏ธ ' : 'โ
'} ${st.step}: ${st.status}${st.reason ? ' โ ' + st.reason : ''}`);
|
|
3223
|
+
for (const w of result.summary.warnings) console.log(` โ ๏ธ ${w}`);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3487
3226
|
return result;
|
|
3488
3227
|
}
|
|
3489
3228
|
|
|
@@ -3509,6 +3248,7 @@ Options:
|
|
|
3509
3248
|
--keep N ไฟ็ๆๆฐ N ๅ done ไปปๅ๏ผ้ ่จญ 10๏ผ
|
|
3510
3249
|
--skip-archive ่ทณ้ archive ๆชขๆฅ
|
|
3511
3250
|
--skip-git ่ทณ้ git ๅๆฏ็ฎก็
|
|
3251
|
+
--no-source ็ก source ไปปๅ๏ผruntime/config ไฟฎๅพฉ๏ผ็ก branch๏ผ๏ผgit merge/verify ๆจ่จ skipped
|
|
3512
3252
|
--skip-tests ่ทณ้ๅๆญธๆธฌ่ฉฆ๏ผPPL-113๏ผ
|
|
3513
3253
|
--test-cmd "CMD" Agent ๆๅฎ็ๆธฌ่ฉฆๆไปค๏ผๅฏๅคๆฌกๆๅฎ๏ผPPL-113๏ผ
|
|
3514
3254
|
--message "MSG" ่ช่จ commit message
|
|
@@ -3562,6 +3302,7 @@ Examples:
|
|
|
3562
3302
|
const keep = getArg('--keep') ? parseInt(getArg('--keep'), 10) : CONFIG.DEFAULT_KEEP;
|
|
3563
3303
|
const skipArchive = args.includes('--skip-archive');
|
|
3564
3304
|
const skipGit = args.includes('--skip-git');
|
|
3305
|
+
const noSource = args.includes('--no-source');
|
|
3565
3306
|
const skipTests = args.includes('--skip-tests');
|
|
3566
3307
|
const dryRun = args.includes('--dry-run');
|
|
3567
3308
|
const message = getArg('--message');
|
|
@@ -3584,7 +3325,7 @@ Examples:
|
|
|
3584
3325
|
}
|
|
3585
3326
|
|
|
3586
3327
|
try {
|
|
3587
|
-
postComplete({ taskId, keep, skipArchive, skipGit, skipTests, dryRun, message, projectPath, testCommands, worktreePath });
|
|
3328
|
+
postComplete({ taskId, keep, skipArchive, skipGit, skipTests, dryRun, message, projectPath, testCommands, worktreePath, noSource });
|
|
3588
3329
|
} catch (e) {
|
|
3589
3330
|
console.error(`โ Post-complete failed: ${e.message}`);
|
|
3590
3331
|
process.exit(1);
|
|
@@ -3593,58 +3334,13 @@ Examples:
|
|
|
3593
3334
|
// ?????????????????????????????????????????????????????????????????????????????
|
|
3594
3335
|
// SECTION: EXPORTS
|
|
3595
3336
|
// ?????????????????????????????????????????????????????????????????????????????
|
|
3596
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3597
|
-
// Sub-agent post-complete (lightweight equivalent for Agent tool dispatch)
|
|
3598
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3599
|
-
|
|
3600
|
-
/**
|
|
3601
|
-
* Lightweight post-complete for tasks dispatched via Agent tool (isolation=worktree).
|
|
3602
|
-
* The Agent tool handles worktree git isolation automatically, so git merge is skipped.
|
|
3603
|
-
* Health scoring is deferred to the next Heimdall cycle (dashboard must be running
|
|
3604
|
-
* for measureHealthScoreSync to work; safe to call from Ansuz interactive session).
|
|
3605
|
-
*
|
|
3606
|
-
* Call this after the Agent tool sub-agent returns to record the health delta.
|
|
3607
|
-
*
|
|
3608
|
-
* @param {string} taskId - Task ID (TASK_XXX)
|
|
3609
|
-
* @param {string|null} projectPath - Sub-project path (null = core)
|
|
3610
|
-
* @returns {Promise<{ healthDelta: number|null, grade: string|null }>}
|
|
3611
|
-
*/
|
|
3612
|
-
async function subagentPostComplete(taskId, projectPath) {
|
|
3613
|
-
const result = { healthDelta: null, grade: null };
|
|
3614
|
-
|
|
3615
|
-
try {
|
|
3616
|
-
// Record post-task health snapshot (for audit trail + Heimdall comparison)
|
|
3617
|
-
const postHealth = measureHealthScoreSync(projectPath);
|
|
3618
|
-
if (postHealth && taskId) {
|
|
3619
|
-
saveTaskHealthSnapshot(taskId, 'after', postHealth, projectPath);
|
|
3620
|
-
const preSn = readPreTaskHealthSnapshot(taskId, projectPath);
|
|
3621
|
-
if (preSn?.score != null) {
|
|
3622
|
-
// TASK_2341: null when the baseline is graph-era (different score_version)
|
|
3623
|
-
result.healthDelta = computeHealthVerdict(preSn, postHealth).delta;
|
|
3624
|
-
result.grade = postHealth.grade;
|
|
3625
|
-
console.log(
|
|
3626
|
-
` ๐ก๏ธ Sub-agent Guardian: health=${postHealth.score}/100 (${postHealth.grade})` +
|
|
3627
|
-
` trend=${typeof result.healthDelta === 'number' ? (result.healthDelta >= 0 ? '+' : '') + result.healthDelta : 'n/a'}`
|
|
3628
|
-
);
|
|
3629
|
-
} else {
|
|
3630
|
-
// Save as baseline for future comparison
|
|
3631
|
-
saveTaskHealthSnapshot(taskId, 'before', postHealth, projectPath);
|
|
3632
|
-
result.grade = postHealth.grade;
|
|
3633
|
-
console.log(` ๐ก๏ธ Sub-agent Guardian: health=${postHealth.score}/100 (${postHealth.grade}) โ saved as baseline`);
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
|
-
} catch { /* never block Ansuz on health check failure */ }
|
|
3637
|
-
|
|
3638
|
-
return result;
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
3337
|
module.exports = {
|
|
3642
3338
|
classifyMergeFailureReason,
|
|
3339
|
+
summarizePostComplete,
|
|
3340
|
+
spawnFailureCause,
|
|
3643
3341
|
postComplete,
|
|
3644
3342
|
archiveCheck,
|
|
3645
3343
|
gitBranchAndMerge,
|
|
3646
|
-
measureHealthScoreSync,
|
|
3647
|
-
saveTaskHealthSnapshot,
|
|
3648
3344
|
classifySkillKey,
|
|
3649
3345
|
isDaemonManagedPath,
|
|
3650
3346
|
listDaemonManagedPaths,
|
|
@@ -3653,7 +3349,6 @@ module.exports = {
|
|
|
3653
3349
|
checkAndUpdatePostCompleteGuard,
|
|
3654
3350
|
updatePostCompleteGuardOutcome,
|
|
3655
3351
|
writeInboxEntryIdempotent,
|
|
3656
|
-
subagentPostComplete,
|
|
3657
3352
|
readLatestHandoff, // TASK_1828: handoffs/-first resolver for relay summaries
|
|
3658
3353
|
reclaimWorktreeDir, // TASK_2028: unlock + git-level reclaim for stale/interrupted worktrees
|
|
3659
3354
|
appendPhase9OutcomeLog, // TASK_2078: exported for agent_result/transcript precedence unit testing
|