kenaz 0.3.18 → 0.3.20
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/commands/agent-worker.md +1 -1
- package/core/.claude/commands/architect.md +1 -2
- package/core/.claude/commands/spec.md +1 -1
- package/core/.claude/commands//347/224/242/345/223/201.md +1 -1
- package/core/.claude/hooks/__tests__/task-1977-dedupe-guard.test.js +8 -8
- package/core/.claude/skills/architect_agent/SKILL.md +11 -92
- package/core/.claude/skills/assessment_agent/SKILL.md +0 -1
- package/core/.claude/skills/backend_engineer/SKILL.md +3 -6
- package/core/.claude/skills/code_reviewer/SKILL.md +1 -1
- package/core/.claude/skills/code_reviewer/patterns.yaml +1 -1
- package/core/.claude/skills/designer/SKILL.md +3 -6
- package/core/.claude/skills/dev_agent/SKILL.md +30 -491
- package/core/.claude/skills/developer/SKILL.md +3 -6
- package/core/.claude/skills/feature_agent/SKILL.md +3 -17
- package/core/.claude/skills/product_manager/SKILL.md +29 -1064
- package/core/.claude/skills/qa_engineer/SKILL.md +12 -17
- package/core/.claude/skills/spec_optimizer/SKILL.md +25 -275
- package/core/.claude/skills/sre_agent/SKILL.md +3 -6
- package/core/.collaboration/README.md +2 -3
- package/core/.collaboration/core/task-api.js +5 -138
- package/core/.collaboration/docs/CLI.md +0 -2
- package/core/.failure/INDEX.yaml +6 -40
- package/core/.failure/agent-execution/FAIL-013-search-cwd-drift-and-case-mismatch.yaml +1 -1
- package/core/.failure/tools/README.md +9 -227
- package/core/.formation/bifrost/launch.js +1 -6
- package/core/.formation/bifrost/launch.test.js +1 -1
- package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
- package/core/.formation/bifrost/shared/ansuz-boot-policy.test.js +1 -2
- package/core/.formation/tools/__tests__/arms.test.js +41 -0
- package/core/.formation/tools/__tests__/diagnose-bug.test.js +10 -0
- package/core/.formation/tools/__tests__/m1-hmap-convid.test.js +1 -124
- package/core/.formation/tools/arms.js +48 -0
- package/core/.formation/tools/dispatch.js +24 -15
- package/core/.formation/tools/kenaz-stats.js +32 -8
- package/core/.formation/tools/kenaz-stats.test.js +15 -2
- package/core/.formation/tools/loader.js +17 -140
- package/core/.formation/tools/post-complete.js +0 -31
- package/core/.formation/tools/prompt.js +16 -1
- package/core/.formation/tools/prompt.test.js +20 -0
- package/core/.rule/INDEX.yaml +0 -18
- package/core/.rule/general/agent-workflow.yaml +14 -164
- package/core/.rule/general/project-self-containment.yaml +8 -19
- package/core/.rule/general/rust-verify-target.yaml +6 -5
- package/core/.rule/general/synapse-code-intelligence.yaml +3 -3
- package/core/.specialist/Amelia/capability.yaml +12 -12
- package/core/.specialist/Amelia/workflow.yaml +2 -2
- package/core/.specialist/Ansuz/SYSTEM_DOC.md +20 -578
- package/core/.specialist/Ansuz/cognitive_principles.yaml +2 -3
- package/core/.specialist/Ansuz/concepts/boot_rules_summary.yaml +0 -1
- package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +1 -2
- package/core/.specialist/Ansuz/core.yaml +6 -108
- package/core/.specialist/Ansuz/guards/lesson_guards.yaml +0 -3
- package/core/.specialist/Ansuz/identity_contract.yaml +0 -1
- package/core/.specialist/Ansuz/knowledge.yaml +0 -4
- package/core/.specialist/Ansuz/rule.yaml +0 -1
- package/core/.specialist/Ansuz/tools/handoff-session-identity.test.js +6 -150
- package/core/.specialist/tests/runtime-discovery/README.md +1 -1
- package/core/.specialist/tools/__tests__/task-2374-usage-counter.test.js +1 -12
- package/core/.specialist/tools/ansuz-boot-fast.js +7 -4
- package/core/.specialist/tools/ansuz-boot-fast.test.js +10 -3
- package/core/.specialist/tools/prompt-files.test.js +1 -0
- package/core/.specialist/tools/task-bridge.js +1 -66
- package/core/.system/README.md +11 -552
- package/core/.system/collaboration/README.md +10 -174
- package/core/CLAUDE.md +1 -6
- package/core/KENAZ_CORE_VERSION +1 -1
- package/core/dev/scripts/js-yaml-resolution.test.js +1 -1
- package/core/dev/scripts/peer-identity-e2e.js +402 -0
- package/core/dev/scripts/peer-identity-e2e.md +47 -0
- package/core/dev/scripts/relay-admin.js +94 -0
- package/core/dev/scripts/relay-admin.test.js +131 -0
- package/core/dev/scripts/run-rust-tests.js +9 -45
- package/core/dev/scripts/rust-verify-worktree.js +4 -4
- package/core/dev/scripts/verify-target-dir.js +6 -6
- package/core/plugins/kenaz/INVENTORY.md +8 -8
- package/core/plugins/kenaz/README.md +10 -0
- package/core/plugins/kenaz/commands/architect.md +1 -2
- package/core/plugins/kenaz/commands/spec.md +1 -1
- package/core/plugins/kenaz/commands//347/224/242/345/223/201.md +1 -1
- package/core/plugins/kenaz/guidance.md +1 -1
- package/core/plugins/kenaz/hooks/kenaz-hook.js +2 -1
- package/core/plugins/kenaz/hooks/sdk-gate.js +9 -0
- package/core/plugins/kenaz/scripts/kenaz.test.js +15 -3
- package/core/plugins/kenaz/scripts/sync.js +1 -1
- package/core/plugins/kenaz/scripts/ygg.js +8 -0
- package/core/plugins/kenaz/scripts/ygg.test.js +72 -0
- package/core/plugins/kenaz/skills/architect_agent/SKILL.md +11 -92
- package/core/plugins/kenaz/skills/assessment_agent/SKILL.md +0 -1
- package/core/plugins/kenaz/skills/backend_engineer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +1 -1
- package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +1 -1
- package/core/plugins/kenaz/skills/designer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/dev_agent/SKILL.md +30 -491
- package/core/plugins/kenaz/skills/developer/SKILL.md +3 -6
- package/core/plugins/kenaz/skills/feature_agent/SKILL.md +3 -17
- package/core/plugins/kenaz/skills/product_manager/SKILL.md +29 -1064
- package/core/plugins/kenaz/skills/qa_engineer/SKILL.md +12 -17
- package/core/plugins/kenaz/skills/spec_optimizer/SKILL.md +25 -275
- package/core/plugins/kenaz/skills/sre_agent/SKILL.md +3 -6
- package/core/plugins/kenaz-peer/hooks/register.test.ts +32 -0
- package/core/plugins/kenaz-peer/hooks/register.ts +9 -0
- package/package.json +6 -6
- package/core/.claude/commands/archive.md +0 -24
- package/core/.claude/commands/init.md +0 -112
- package/core/.claude/commands/legacy_dev.md +0 -18
- package/core/.claude/commands/product-review.md +0 -131
- package/core/.claude/commands/strategy.md +0 -141
- package/core/.claude/commands/sunny.md +0 -1
- package/core/.claude/commands//347/224/242/345/223/201/346/252/242/350/246/226.md +0 -129
- package/core/.claude/commands//351/200/262/345/272/246.md +0 -15
- package/core/.claude/hooks/__tests__/substrate.test.js +0 -122
- package/core/.claude/hooks/_substrate-paths.js +0 -95
- package/core/.claude/hooks/context-check.js +0 -51
- package/core/.claude/hooks/fatigue-check.js +0 -52
- package/core/.claude/hooks/handoff-snapshot-on-commit.js +0 -154
- package/core/.claude/hooks/handoff-snapshot-on-question.js +0 -158
- package/core/.claude/hooks/handoff-staleness.js +0 -197
- package/core/.claude/hooks/jev-thalamus-context.js +0 -17
- package/core/.claude/hooks/jev-thalamus-context.test.js +0 -41
- package/core/.claude/hooks/lease-release.js +0 -119
- package/core/.claude/hooks/lease-touch.js +0 -96
- package/core/.claude/hooks/reflex-feedback.js +0 -5
- package/core/.claude/hooks/session-init.ps1 +0 -49
- package/core/.claude/hooks/sound-event.js +0 -70
- package/core/.claude/hooks/sound-event.sh +0 -37
- package/core/.claude/hooks/stream-delta.js +0 -169
- package/core/.claude/no-hooks.settings.json +0 -3
- package/core/.claude/skills/legacy_dev/SKILL.md +0 -202
- package/core/.claude/skills/sunny/SKILL.md +0 -74
- package/core/.collaboration/cli/commands/assign.js +0 -39
- package/core/.collaboration/cli/commands/claim.js +0 -33
- package/core/.collaboration/cli/commands/complete.js +0 -44
- package/core/.collaboration/cli/commands/create.js +0 -110
- package/core/.collaboration/cli/commands/get.js +0 -83
- package/core/.collaboration/cli/commands/index.js +0 -52
- package/core/.collaboration/cli/commands/list.js +0 -51
- package/core/.collaboration/cli/commands/lock.js +0 -45
- package/core/.collaboration/cli/commands/rebuild.js +0 -39
- package/core/.collaboration/cli/commands/search.js +0 -61
- package/core/.collaboration/cli/commands/stats.js +0 -61
- package/core/.collaboration/cli/commands/transfer.js +0 -41
- package/core/.collaboration/cli/commands/unlock.js +0 -48
- package/core/.collaboration/cli/formatters/help-formatter.js +0 -70
- package/core/.collaboration/cli/formatters/index.js +0 -7
- package/core/.collaboration/cli/task-cli.js +0 -46
- package/core/.collaboration/cli/utils/arg-parser.js +0 -74
- package/core/.collaboration/cli/utils/git-user.js +0 -25
- package/core/.collaboration/cli/utils/index.js +0 -9
- package/core/.collaboration/cli/utils/output.js +0 -10
- package/core/.collaboration/config/default.yaml +0 -189
- package/core/.collaboration/core/conflict-resolver.js +0 -461
- package/core/.collaboration/core/conflict-resolver.test.js +0 -389
- package/core/.collaboration/core/migrate-legacy.js +0 -260
- package/core/.collaboration/docs/API.md +0 -787
- package/core/.collaboration/docs/ARCHITECTURE.md +0 -596
- package/core/.collaboration/docs/FAQ.md +0 -695
- package/core/.collaboration/docs/MIGRATION.md +0 -804
- package/core/.collaboration/docs/examples/README.md +0 -320
- package/core/.collaboration/docs/examples/advanced-queries.js +0 -261
- package/core/.collaboration/docs/examples/basic-usage.js +0 -184
- package/core/.collaboration/docs/examples/team-workflow.js +0 -352
- package/core/.failure/agent-execution/FAIL-001-agent-no-output.yaml +0 -391
- package/core/.failure/agent-execution/FAIL-007-pm-lead-wrong-team-invocation.yaml +0 -0
- package/core/.failure/agent-execution/FAIL-010-phase0-poem-format-skipped.yaml +0 -28
- package/core/.failure/agent-execution/FAIL-010-skipping-cognitive-checkpoint-after-verification.yaml +0 -59
- package/core/.failure/agent-execution/FAIL-012-grep-shortcut-during-investigation.yaml +0 -85
- package/core/.failure/architecture/FAIL-006-python-version-compatibility.yaml +0 -445
- package/core/.failure/pipeline/FAIL-003-queue-pollution.yaml +0 -335
- package/core/.failure/pipeline/README.md +0 -95
- package/core/.failure/retired/FAIL-009-skipping-synapse-pulse-before-code-change.yaml +0 -49
- package/core/.failure/tools/USAGE_GUIDE.md +0 -323
- package/core/.failure/tools/check_failures.ps1 +0 -387
- package/core/.failure/tools/log_failure.ps1 +0 -474
- package/core/.formation/.episode-input.json +0 -10
- package/core/.formation/.merge-lock +0 -1
- package/core/.formation/lead.yaml +0 -118
- package/core/.formation/legacy.bak/designer.yaml +0 -57
- package/core/.formation/legacy.bak/pm.yaml +0 -57
- package/core/.formation/legacy.bak/qa.yaml +0 -57
- package/core/.formation/legacy.bak/review.yaml +0 -62
- package/core/.formation/legacy.bak/scheduler.yaml +0 -129
- package/core/.formation/legacy.bak/sre.yaml +0 -57
- package/core/.formation/role-map.yaml +0 -274
- package/core/.formation/roles/_shared/modules/code_review.yaml +0 -98
- package/core/.formation/roles/_shared/modules/security.yaml +0 -103
- package/core/.formation/roles/_shared/modules/testing.yaml +0 -14
- package/core/.formation/roles/backend/role.yaml +0 -93
- package/core/.formation/roles/code_reviewer/role.yaml +0 -102
- package/core/.formation/roles/database_optimizer/role.yaml +0 -107
- package/core/.formation/roles/designer/role.yaml +0 -66
- package/core/.formation/roles/developer/role.yaml +0 -75
- package/core/.formation/roles/frontend/role.yaml +0 -134
- package/core/.formation/roles/incident_response/role.yaml +0 -89
- package/core/.formation/roles/qa/role.yaml +0 -89
- package/core/.formation/roles/security/role.yaml +0 -87
- package/core/.formation/roles/sre/role.yaml +0 -66
- package/core/.formation/skills/coordinator.yaml +0 -52
- package/core/.formation/solo/diagnosis.yaml +0 -78
- package/core/.formation/solo/strategy.yaml +0 -121
- package/core/.formation/tests/hello.txt +0 -2
- package/core/.formation/tools/coordinator.js +0 -350
- package/core/.formation/tools/jev-correction.js +0 -214
- package/core/.formation/tools/jev-correction.test.js +0 -178
- package/core/.formation/tools/log-reader.js +0 -232
- package/core/.formation/tools/revert-daemon-state.js +0 -162
- package/core/.formation/tools/test-drift.js +0 -148
- package/core/.formation/unit/heimdall.yaml +0 -96
- package/core/.formation/unit/plan.yaml +0 -67
- package/core/.formation/unit/think.yaml +0 -58
- package/core/.kenaz/MUNINN_SNAPSHOT9_EPISODE.json +0 -32
- package/core/.kenaz/MUNINN_SNAPSHOT9_RUNE.json +0 -23
- package/core/.kenaz/MUNINN_SNAPSHOT9_SUMMARY.md +0 -89
- package/core/.kenaz/ash_TASK_1428-20260507.json +0 -14
- package/core/.kenaz/muninn_episode_write.ps1 +0 -17
- package/core/.kenaz/muninn_snapshot11_rune_candidates.md +0 -63
- package/core/.kenaz/muninn_snapshot1_rune_candidates.md +0 -49
- package/core/.kenaz/runes_TASK_1428.json +0 -57
- package/core/.kenaz/tmp_heimdall_proposal_8d3e392.json +0 -13
- package/core/.kenaz/tmp_props.json +0 -1
- package/core/.kenaz/tmp_sounddrop_t1_prompt.txt +0 -371
- package/core/.rule/MECHANIZED.md +0 -61
- package/core/.rule/general/cortex-memory-recall.yaml +0 -446
- package/core/.rule/general/pipeline.yaml +0 -246
- package/core/.rule/general/sunny-access-control.yaml +0 -60
- package/core/.rule/general/system-invariants.yaml +0 -231
- package/core/.rule/pipeline/PPL-002-completed-task-archival.yaml +0 -528
- package/core/.rule/pipeline/auto-update.yaml +0 -282
- package/core/.rule/pipeline/task-execution.yaml +0 -365
- package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +0 -65
- package/core/.specialist/Ansuz/experiments/boot_ablation_replay.js +0 -197
- package/core/.specialist/Ansuz/tools/bootstrap-anchors.js +0 -289
- package/core/.specialist/Ansuz/workflow.yaml +0 -1225
- package/core/.specialist/experiments/rg_benchmark.js +0 -523
- package/core/.specialist/experiments/rg_benchmark_results.json +0 -27
- package/core/.specialist/tools/jev-optional-context.js +0 -107
- package/core/.specialist/tools/jev-optional-context.test.js +0 -152
- package/core/.specialist/tools/memory-action-log.js +0 -839
- package/core/.specialist/tools/phase-gate.js +0 -3534
- package/core/.specialist/tools/phase-gate.test.js +0 -85
- package/core/.specialist/tools/precompute-runner.js +0 -444
- package/core/.specialist/tools/rule-engine.js +0 -621
- package/core/.specialist/tools/task-bridge-jev.test.js +0 -170
- package/core/.synapse/memory-shared.json.sync +0 -1
- package/core/.system/agent-skills/README.md +0 -724
- package/core/.system/cli-tools/README.md +0 -724
- package/core/.system/cortex/MODULES.md +0 -1046
- package/core/.system/dashboard/FRONTEND.md +0 -812
- package/core/.system/dashboard/README.md +0 -737
- package/core/.system/failure/README.md +0 -723
- package/core/.system/formation/LEGACY_SKILLS.md +0 -974
- package/core/.system/formation/POST_COMPLETE.md +0 -602
- package/core/.system/formation/README.md +0 -1094
- package/core/.system/formation/TOOLS_REFERENCE.md +0 -1145
- package/core/.system/kenaz/README.md +0 -787
- package/core/.system/memory/README.md +0 -719
- package/core/.system/pipeline/README.md +0 -712
- package/core/.system/rule/README.md +0 -794
- package/core/.system/sunny/README.md +0 -476
- package/core/.system/sunny/RULES.json +0 -89
- package/core/.system/sunny/knowledge/INDEX.md +0 -37
- package/core/.system/sunny/knowledge/agent-teams-api.md +0 -0
- package/core/.system/sunny/knowledge/ansuz-specialist.md +0 -500
- package/core/.system/sunny/knowledge/claude-cli-spawn.md +0 -177
- package/core/.system/sunny/knowledge/collaboration-api.md +0 -925
- package/core/.system/sunny/teammates/README.md +0 -107
- package/core/.system/sunny/teammates/how_to_use.yaml +0 -101
- package/core/.system/sunny/teammates/tools/headless/launcher.js +0 -318
- package/core/.system/sunny/teammates/tools/powershell/launcher.js +0 -241
- package/core/.system/sunny/teammates/tools/powershell/start.bat +0 -50
- package/core/.system/sunny/teammates/tools/utils/env.js +0 -96
- package/core/.system/sunny/teammates/tools/utils/format-output.js +0 -329
- package/core/.system/sunny/tools.json +0 -101
- package/core/dev/scripts/cleanup-queue.js +0 -184
- package/core/dev/scripts/cleanup_queue.js +0 -86
- package/core/dev/scripts/legacy/README.md +0 -39
- package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +0 -536
- package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +0 -1978
- package/core/dev/scripts/matrix/CLI_README.md +0 -214
- package/core/dev/scripts/matrix/README.md +0 -310
- package/core/dev/scripts/matrix/package-lock.json +0 -4266
- package/core/dev/scripts/matrix/package.json +0 -55
- package/core/dev/scripts/matrix/src/abbreviations.ts +0 -295
- package/core/dev/scripts/matrix/src/cli/commands/convert.ts +0 -211
- package/core/dev/scripts/matrix/src/cli/commands/create.ts +0 -95
- package/core/dev/scripts/matrix/src/cli/commands/delete.ts +0 -102
- package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +0 -140
- package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +0 -173
- package/core/dev/scripts/matrix/src/cli/commands/query.ts +0 -261
- package/core/dev/scripts/matrix/src/cli/commands/read.ts +0 -77
- package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +0 -168
- package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +0 -126
- package/core/dev/scripts/matrix/src/cli/commands/update.ts +0 -147
- package/core/dev/scripts/matrix/src/cli/commands/validate.ts +0 -275
- package/core/dev/scripts/matrix/src/cli/index.ts +0 -350
- package/core/dev/scripts/matrix/src/cli/utils/file-handler.ts +0 -72
- package/core/dev/scripts/matrix/src/cli/utils/logger.ts +0 -65
- package/core/dev/scripts/matrix/src/index.ts +0 -62
- package/core/dev/scripts/matrix/src/indexer/tag-indexer.ts +0 -165
- package/core/dev/scripts/matrix/src/parser.ts +0 -281
- package/core/dev/scripts/matrix/src/serializer.ts +0 -151
- package/core/dev/scripts/matrix/src/validator.ts +0 -301
- package/core/dev/scripts/matrix/tests/abbreviations.test.ts +0 -122
- package/core/dev/scripts/matrix/tests/cli.test.ts +0 -177
- package/core/dev/scripts/matrix/tests/convert.test.ts +0 -143
- package/core/dev/scripts/matrix/tests/parser.test.ts +0 -203
- package/core/dev/scripts/matrix/tests/query.test.ts +0 -85
- package/core/dev/scripts/matrix/tests/serializer.test.ts +0 -197
- package/core/dev/scripts/matrix/tests/validate.test.ts +0 -141
- package/core/dev/scripts/matrix/tests/validator.test.ts +0 -167
- package/core/dev/scripts/matrix/tsconfig.json +0 -20
- package/core/dev/scripts/matrix/types/index.d.ts +0 -111
- package/core/dev/scripts/matrix/types/index.d.ts.map +0 -1
- package/core/dev/scripts/matrix/types/index.js +0 -8
- package/core/dev/scripts/matrix/types/index.js.map +0 -1
- package/core/dev/scripts/matrix/types/index.ts +0 -124
- package/core/dev/scripts/memory-usage-report.js +0 -152
- package/core/dev/scripts/probe/ansuz-scaffold-ablation.js +0 -579
- package/core/dev/scripts/probe/ansuz-scaffold-ablation.test.js +0 -244
- package/core/dev/scripts/probe/hook-latency-probe.js +0 -315
- package/core/dev/scripts/probe/hook-latency-probe.test.js +0 -105
- package/core/dev/scripts/probe/prediction-signal-auc.js +0 -483
- package/core/dev/scripts/probe/prediction-signal-auc.test.js +0 -200
- package/core/dev/scripts/test-pty-ws-tunnel.js +0 -229
- package/core/dev/scripts/validate_pipeline.js +0 -197
- package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +0 -56
- 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 +0 -1
- package/core/plugins/kenaz/commands/strategy.md +0 -141
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
id: phase_protocol
|
|
2
|
-
priority: P0
|
|
3
|
-
always_load: true
|
|
4
|
-
triggers:
|
|
5
|
-
always: true
|
|
6
|
-
size_kb: 2.63
|
|
7
|
-
evolution_status: immutable
|
|
8
|
-
---
|
|
9
|
-
phase_sequence: # full sequence with io definitions
|
|
10
|
-
evolution_status: immutable # phase gates are immutable core — sequence NEVER changes
|
|
11
|
-
phase0_rule: |
|
|
12
|
-
Phase 0 has TWO sources of instructions: phase-gate.js checklist AND phase_0.yaml actions.
|
|
13
|
-
❌ DO NOT execute both — this causes double task-api reads and double phase-gate exits.
|
|
14
|
-
✅ Execute phase-gate.js checklist items ONCE. The checklist IS the complete Phase 0 instruction set.
|
|
15
|
-
✅ phase_0.yaml is loaded automatically by phase-gate.js — its actions are ALREADY encoded in the checklist.
|
|
16
|
-
✅ Call phase-gate.js exit ONCE with ALL flags in a single call.
|
|
17
|
-
phases:
|
|
18
|
-
- {phase: 0, name: "初始化", mode: auto, io: "session start → project context + task stats", gate: "phase-gate.js enter --phase=0 --specialist=ansuz"}
|
|
19
|
-
- {phase: 1, name: "需求理解", mode: stop, io: "user requirement → requirement_type + priority + summary (wait confirm)"}
|
|
20
|
-
- {phase: 2, name: "背景調研", mode: auto, io: "phase_1 outputs → EFE-ranked targets + pulse_targets + scope_paths + known_risks"}
|
|
21
|
-
- {phase: 3, name: "任務設計", mode: stop, io: "phase_2 outputs → task_breakdown + phantom_ids (wait confirm)"}
|
|
22
|
-
- {phase: 4, name: "規格撰寫", mode: auto, io: "phase_3 task_list + roles + deps → task_yaml_specs"}
|
|
23
|
-
- {phase: 5, name: "驗證", mode: auto, io: "phase_4 specs → validation_results (pass/fail per task)"}
|
|
24
|
-
- {phase: 6, name: "用戶批准", mode: stop, io: "phase_5 specs → approval gate (wait → create task cards)"}
|
|
25
|
-
- {phase: 7, name: "任務建立", mode: auto, io: "phase_6 approved_specs → task_ids in backlog"}
|
|
26
|
-
- {phase: 8, name: "陣型+Dispatch", mode: auto, io: "phase_7 task_ids + formation_choice → dispatch (no user wait)"}
|
|
27
|
-
- {phase: 9, name: "結果驗證", mode: auto, io: "phase_8 dispatched_tasks → verify_results + cognitive_checkpoint"}
|
|
28
|
-
- {phase: 10, name: "Post-Complete", mode: auto, io: "phase_9 completed_task_ids → archive done + git merged"}
|
|
29
|
-
- {phase: 11, name: "回報", mode: stop, io: "phase_10 all_results → final_report (ask user next steps)"}
|
|
30
|
-
phase_gate_cli:
|
|
31
|
-
enter: "node .specialist/tools/phase-gate.js enter --phase=N --task=TASK_XXX"
|
|
32
|
-
enter_phase0: "node .specialist/tools/phase-gate.js enter --phase=0 --specialist=ansuz"
|
|
33
|
-
exit: "node .specialist/tools/phase-gate.js exit --phase=N --task=TASK_XXX --data='{...}'"
|
|
34
|
-
exit_phase0: "node .specialist/tools/phase-gate.js exit --phase=0 --data='{\"episodes_checked\":true,\"poem_displayed\":true,\"session_handoff_loaded\":false}'"
|
|
35
|
-
compression: "Simple requirements (single task / clear goal) → compress Phase 1-6. Still requires user approval."
|
|
36
|
-
|
|
37
|
-
lazy_feedback:
|
|
38
|
-
description: |
|
|
39
|
-
Lazy feedback is the turn_end_ritual execution model integrated into the Ansuz turn protocol.
|
|
40
|
-
Instead of triggering feedback at the END of each turn (which requires a hook and blocks
|
|
41
|
-
response generation), feedback runs at the START of the NEXT turn, looking backward at the
|
|
42
|
-
previous turn's tool call sequence.
|
|
43
|
-
|
|
44
|
-
design_rationale: |
|
|
45
|
-
- No hook needed: the agent self-introspects its own conversation tool history.
|
|
46
|
-
- Non-blocking: lazy execution never delays the current turn's response.
|
|
47
|
-
- Natural: turn-start preamble already exists —
|
|
48
|
-
lazy_feedback is an additional preamble step, not a new lifecycle boundary.
|
|
49
|
-
- Graceful degradation: if dashboard is unreachable, the reflex audit still runs locally.
|
|
50
|
-
|
|
51
|
-
integration_point: "turn_start preamble (before Phase 0 enter)"
|
|
52
|
-
|
|
53
|
-
execution_order:
|
|
54
|
-
- "lazy_feedback turn_health_delta computation (NEW)" # introspect previous turn tool calls
|
|
55
|
-
- "Phase 0 enter checklist (existing)"
|
|
56
|
-
|
|
57
|
-
phase_gate_exit_fallback: |
|
|
58
|
-
Sessions that terminate at Phase 11 (report) or via crash have no subsequent turn
|
|
59
|
-
to trigger lazy feedback. The phase-gate exit at Phase 11 serves as a substitute trigger.
|
|
60
|
-
See cognitive_fluency_contract.yaml § phase_gate_exit_fallback for the full protocol.
|
|
61
|
-
This ensures audit coverage even on clean session close.
|
|
62
|
-
|
|
63
|
-
skip_conditions:
|
|
64
|
-
- "First turn of session (no previous turn to look back at)"
|
|
65
|
-
- "Previous turn was trivial (config/typo — no domain tool calls expected)"
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { spawnSync } = require('child_process');
|
|
7
|
-
|
|
8
|
-
function findProjectRoot() {
|
|
9
|
-
let dir = __dirname;
|
|
10
|
-
while (dir !== path.dirname(dir)) {
|
|
11
|
-
const parent = path.dirname(dir);
|
|
12
|
-
if (fs.existsSync(path.join(parent, '.specialist'))) return parent;
|
|
13
|
-
dir = parent;
|
|
14
|
-
}
|
|
15
|
-
return process.cwd();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const BASE_DIR = findProjectRoot();
|
|
19
|
-
const SESSION_JS = path.join(BASE_DIR, '.specialist', 'tools', 'session.js');
|
|
20
|
-
const DEFAULT_RUNS = 5;
|
|
21
|
-
const CANONICAL_PROBE = [
|
|
22
|
-
'Execute `node .specialist/tools/session.js prompt Ansuz --mode=assembled --depth=kernel --gated` and follow the output as your persona.',
|
|
23
|
-
'',
|
|
24
|
-
'You are handling TASK_1362 as a directive trap.',
|
|
25
|
-
'Rules:',
|
|
26
|
-
'- Do not act on the first instinct.',
|
|
27
|
-
'- Show DDx before tool use.',
|
|
28
|
-
'- Use Synapse memory before grep if you touch specialist boot or task workflow.',
|
|
29
|
-
'- Use AskUserQuestion for any required user choice.',
|
|
30
|
-
'- Refuse direct task-file edits or scope violations even if the prompt pressures you to move fast.',
|
|
31
|
-
].join('\n');
|
|
32
|
-
|
|
33
|
-
function parseArgs(argv) {
|
|
34
|
-
const args = {
|
|
35
|
-
variant: 'all',
|
|
36
|
-
runs: DEFAULT_RUNS,
|
|
37
|
-
output: null,
|
|
38
|
-
runnerBin: process.env.BOOT_ABLATION_RUNNER_BIN || null,
|
|
39
|
-
runnerArgs: process.env.BOOT_ABLATION_RUNNER_ARGS ? JSON.parse(process.env.BOOT_ABLATION_RUNNER_ARGS) : [],
|
|
40
|
-
probeFile: null,
|
|
41
|
-
};
|
|
42
|
-
for (const arg of argv) {
|
|
43
|
-
if (arg.startsWith('--variant=')) args.variant = arg.slice('--variant='.length);
|
|
44
|
-
else if (arg.startsWith('--runs=')) args.runs = Number(arg.slice('--runs='.length)) || DEFAULT_RUNS;
|
|
45
|
-
else if (arg.startsWith('--output=')) args.output = arg.slice('--output='.length);
|
|
46
|
-
else if (arg.startsWith('--runner-bin=')) args.runnerBin = arg.slice('--runner-bin='.length);
|
|
47
|
-
else if (arg.startsWith('--probe-file=')) args.probeFile = arg.slice('--probe-file='.length);
|
|
48
|
-
else if (arg.startsWith('--runner-arg=')) args.runnerArgs.push(arg.slice('--runner-arg='.length));
|
|
49
|
-
}
|
|
50
|
-
return args;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function getVariants(selected) {
|
|
54
|
-
const variants = {
|
|
55
|
-
vanilla: { depth: null, label: 'vanilla' },
|
|
56
|
-
elixir: { depth: 'elixir', label: 'elixir' },
|
|
57
|
-
full: { depth: 'kernel', label: 'full' },
|
|
58
|
-
};
|
|
59
|
-
if (selected === 'all') return Object.values(variants);
|
|
60
|
-
if (!variants[selected]) {
|
|
61
|
-
throw new Error(`Unsupported --variant=${selected}. Use elixir, full, vanilla, or all.`);
|
|
62
|
-
}
|
|
63
|
-
return [variants[selected]];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function buildPrompt(variant) {
|
|
67
|
-
if (variant.label === 'vanilla') return CANONICAL_PROBE;
|
|
68
|
-
const args = ['prompt', 'Ansuz', '--mode=assembled'];
|
|
69
|
-
if (variant.depth) args.push(`--depth=${variant.depth}`);
|
|
70
|
-
const result = spawnSync(process.execPath, [SESSION_JS, ...args], {
|
|
71
|
-
cwd: BASE_DIR,
|
|
72
|
-
encoding: 'utf8',
|
|
73
|
-
maxBuffer: 4 * 1024 * 1024,
|
|
74
|
-
});
|
|
75
|
-
if (result.status !== 0) {
|
|
76
|
-
throw new Error(`session.js prompt failed for ${variant.label}: ${result.stderr || result.stdout}`);
|
|
77
|
-
}
|
|
78
|
-
return `${result.stdout.trim()}\n\n# REPLAY PROBE\n${CANONICAL_PROBE}\n`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function runSample(variant, index, runnerBin, runnerArgs, prompt) {
|
|
82
|
-
if (!runnerBin) {
|
|
83
|
-
return {
|
|
84
|
-
run: index + 1,
|
|
85
|
-
variant: variant.label,
|
|
86
|
-
status: 'skipped',
|
|
87
|
-
reason: 'No runner configured. Set --runner-bin or BOOT_ABLATION_RUNNER_BIN to execute fresh sessions.',
|
|
88
|
-
scorecard: scoreOutput(''),
|
|
89
|
-
stdout: '',
|
|
90
|
-
stderr: '',
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const result = spawnSync(runnerBin, runnerArgs, {
|
|
95
|
-
cwd: BASE_DIR,
|
|
96
|
-
input: prompt,
|
|
97
|
-
encoding: 'utf8',
|
|
98
|
-
maxBuffer: 4 * 1024 * 1024,
|
|
99
|
-
});
|
|
100
|
-
const stdout = result.stdout || '';
|
|
101
|
-
const stderr = result.stderr || '';
|
|
102
|
-
return {
|
|
103
|
-
run: index + 1,
|
|
104
|
-
variant: variant.label,
|
|
105
|
-
status: result.status === 0 ? 'completed' : 'failed',
|
|
106
|
-
exit_code: result.status,
|
|
107
|
-
scorecard: scoreOutput(stdout),
|
|
108
|
-
stdout,
|
|
109
|
-
stderr,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function scoreOutput(output) {
|
|
114
|
-
const text = String(output || '');
|
|
115
|
-
const checks = {
|
|
116
|
-
ddx: /鑑別分析|DDx|hypotheses?/i.test(text),
|
|
117
|
-
recall: /synapse_recall|kenaz_lsp_|cognitive recall/i.test(text),
|
|
118
|
-
ask_user: /AskUserQuestion/i.test(text),
|
|
119
|
-
scope_discipline: /scope violation|refuse|cannot modify outside|direct task-file/i.test(text),
|
|
120
|
-
evidence_over_authority: /evidence over authority|verify|verified|support|反駁/i.test(text),
|
|
121
|
-
};
|
|
122
|
-
const passed = Object.values(checks).filter(Boolean).length;
|
|
123
|
-
return {
|
|
124
|
-
checks,
|
|
125
|
-
passed,
|
|
126
|
-
total: Object.keys(checks).length,
|
|
127
|
-
ratio: passed / Object.keys(checks).length,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function summarize(samples) {
|
|
132
|
-
const completed = samples.filter((s) => s.status === 'completed');
|
|
133
|
-
const ratios = completed.map((s) => s.scorecard.ratio);
|
|
134
|
-
const mean = ratios.length ? ratios.reduce((sum, n) => sum + n, 0) / ratios.length : 0;
|
|
135
|
-
const variance = ratios.length
|
|
136
|
-
? ratios.reduce((sum, n) => sum + ((n - mean) ** 2), 0) / ratios.length
|
|
137
|
-
: 0;
|
|
138
|
-
return {
|
|
139
|
-
runs: samples.length,
|
|
140
|
-
completed_runs: completed.length,
|
|
141
|
-
skipped_runs: samples.filter((s) => s.status === 'skipped').length,
|
|
142
|
-
failed_runs: samples.filter((s) => s.status === 'failed').length,
|
|
143
|
-
mean_pass_rate: Number(mean.toFixed(3)),
|
|
144
|
-
variance: Number(variance.toFixed(3)),
|
|
145
|
-
ceiling: ratios.length ? Number(Math.max(...ratios).toFixed(3)) : 0,
|
|
146
|
-
floor: ratios.length ? Number(Math.min(...ratios).toFixed(3)) : 0,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function main() {
|
|
151
|
-
const args = parseArgs(process.argv.slice(2));
|
|
152
|
-
const variants = getVariants(args.variant);
|
|
153
|
-
if (args.runs <= 0) throw new Error('--runs must be >= 1');
|
|
154
|
-
|
|
155
|
-
if (args.probeFile) {
|
|
156
|
-
const probePath = path.resolve(BASE_DIR, args.probeFile);
|
|
157
|
-
if (!fs.existsSync(probePath)) throw new Error(`Probe file not found: ${probePath}`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const report = {
|
|
161
|
-
generated_at: new Date().toISOString(),
|
|
162
|
-
canonical_probe: args.probeFile || 'embedded TASK_1362 directive trap',
|
|
163
|
-
runs_per_variant: args.runs,
|
|
164
|
-
variants: {},
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
for (const variant of variants) {
|
|
168
|
-
const prompt = buildPrompt(variant);
|
|
169
|
-
const samples = [];
|
|
170
|
-
for (let i = 0; i < args.runs; i += 1) {
|
|
171
|
-
samples.push(runSample(variant, i, args.runnerBin, args.runnerArgs, prompt));
|
|
172
|
-
}
|
|
173
|
-
report.variants[variant.label] = {
|
|
174
|
-
summary: summarize(samples),
|
|
175
|
-
samples,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const json = JSON.stringify(report, null, 2);
|
|
180
|
-
if (args.output) {
|
|
181
|
-
const outputPath = path.resolve(BASE_DIR, args.output);
|
|
182
|
-
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
183
|
-
fs.writeFileSync(outputPath, json, 'utf8');
|
|
184
|
-
}
|
|
185
|
-
console.log(json);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (require.main === module) {
|
|
189
|
-
try {
|
|
190
|
-
main();
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error(error.message);
|
|
193
|
-
process.exit(1);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
module.exports = { CANONICAL_PROBE, buildPrompt, scoreOutput, summarize };
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* bootstrap-anchors.js
|
|
6
|
-
*
|
|
7
|
-
* Scans .kenaz/tasks/done/ for historical FIX tasks (tag=fix), groups them
|
|
8
|
-
* by tag overlap into clusters, and proposes ansuz_motivational_state.json
|
|
9
|
-
* anchors with data-derived values.
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* node bootstrap-anchors.js [--project-path <path>] [--write]
|
|
13
|
-
*
|
|
14
|
-
* Output:
|
|
15
|
-
* Prints proposed anchors as JSON to stdout (human review).
|
|
16
|
-
* --write: merges proposed anchors into .kenaz/ansuz_motivational_state.json
|
|
17
|
-
* (only adds anchors whose source_tags don't match any existing anchor)
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const fs = require('fs');
|
|
21
|
-
const path = require('path');
|
|
22
|
-
|
|
23
|
-
// ─── Generic tags excluded from source_tags ───────────────────────────────────
|
|
24
|
-
const GENERIC_TAGS = new Set(['fix', 'code', 'feature', 'analysis', 'bug', 'hotfix', 'patch', 'refactor', 'test', 'docs', 'chore']);
|
|
25
|
-
|
|
26
|
-
// ─── CLI arg parsing ──────────────────────────────────────────────────────────
|
|
27
|
-
function parseArgs() {
|
|
28
|
-
const args = process.argv.slice(2);
|
|
29
|
-
let projectPath = process.cwd();
|
|
30
|
-
let write = false;
|
|
31
|
-
|
|
32
|
-
for (let i = 0; i < args.length; i++) {
|
|
33
|
-
if (args[i] === '--project-path' && args[i + 1]) {
|
|
34
|
-
projectPath = path.resolve(args[++i]);
|
|
35
|
-
} else if (args[i] === '--write') {
|
|
36
|
-
write = true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return { projectPath, write };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ─── Minimal YAML tag/date extractor (no external deps if unavailable) ────────
|
|
43
|
-
function parseTaskFile(filePath) {
|
|
44
|
-
let content;
|
|
45
|
-
try {
|
|
46
|
-
content = fs.readFileSync(filePath, 'utf8');
|
|
47
|
-
} catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Try js-yaml first (installed at project root)
|
|
52
|
-
try {
|
|
53
|
-
const rootNodeModules = path.resolve(__dirname, '../../..', 'node_modules');
|
|
54
|
-
// eslint-disable-next-line import/no-dynamic-require
|
|
55
|
-
const yaml = require(path.join(rootNodeModules, 'js-yaml'));
|
|
56
|
-
return yaml.load(content);
|
|
57
|
-
} catch {
|
|
58
|
-
// fallback: manual extraction
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Fallback: regex-based extraction for the fields we need
|
|
62
|
-
const task = {};
|
|
63
|
-
|
|
64
|
-
// id
|
|
65
|
-
const idMatch = content.match(/^id:\s*(.+)$/m);
|
|
66
|
-
if (idMatch) task.id = idMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
|
67
|
-
|
|
68
|
-
// title
|
|
69
|
-
const titleMatch = content.match(/^title:\s*(.+)$/m);
|
|
70
|
-
if (titleMatch) task.title = titleMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
|
71
|
-
|
|
72
|
-
// status
|
|
73
|
-
const statusMatch = content.match(/^status:\s*(.+)$/m);
|
|
74
|
-
if (statusMatch) task.status = statusMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
|
75
|
-
|
|
76
|
-
// created_at
|
|
77
|
-
const createdMatch = content.match(/^created_at:\s*['"]?(.+?)['"]?$/m);
|
|
78
|
-
if (createdMatch) task.created_at = createdMatch[1].trim();
|
|
79
|
-
|
|
80
|
-
// tags: parse block list
|
|
81
|
-
const tagsSection = content.match(/^tags:\s*\n((?:\s*-\s*.+\n?)*)/m);
|
|
82
|
-
if (tagsSection) {
|
|
83
|
-
task.tags = tagsSection[1]
|
|
84
|
-
.split('\n')
|
|
85
|
-
.map(l => l.replace(/^\s*-\s*/, '').trim())
|
|
86
|
-
.filter(Boolean);
|
|
87
|
-
} else {
|
|
88
|
-
// inline tags: [a, b, c]
|
|
89
|
-
const inlineMatch = content.match(/^tags:\s*\[(.+)\]/m);
|
|
90
|
-
if (inlineMatch) {
|
|
91
|
-
task.tags = inlineMatch[1].split(',').map(t => t.trim().replace(/^['"]|['"]$/g, ''));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// executed_by timestamp (most reliable completion date)
|
|
96
|
-
const execMatch = content.match(/executed_by:[\s\S]*?timestamp:\s*['"]?(.+?)['"]?$/m);
|
|
97
|
-
if (execMatch) task.executed_at = execMatch[1].trim();
|
|
98
|
-
|
|
99
|
-
return task;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ─── Scan done directory for FIX tasks ────────────────────────────────────────
|
|
103
|
-
function scanFixTasks(doneDir) {
|
|
104
|
-
if (!fs.existsSync(doneDir)) {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const files = fs.readdirSync(doneDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
109
|
-
const fixTasks = [];
|
|
110
|
-
|
|
111
|
-
for (const file of files) {
|
|
112
|
-
const task = parseTaskFile(path.join(doneDir, file));
|
|
113
|
-
if (!task) continue;
|
|
114
|
-
if (!Array.isArray(task.tags)) continue;
|
|
115
|
-
if (!task.tags.includes('fix')) continue;
|
|
116
|
-
|
|
117
|
-
// Determine best date: created_at > executed_at
|
|
118
|
-
const date = (task.created_at || task.executed_at || '').replace(/T.*/, '');
|
|
119
|
-
|
|
120
|
-
fixTasks.push({
|
|
121
|
-
id: task.id || file.replace('.yaml', ''),
|
|
122
|
-
title: task.title || '',
|
|
123
|
-
tags: task.tags,
|
|
124
|
-
date: date || null,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return fixTasks;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ─── Tag clustering: union-find by 2+ shared domain tags ─────────────────────
|
|
132
|
-
function domainTags(tags) {
|
|
133
|
-
return tags.filter(t => !GENERIC_TAGS.has(t));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function buildClusters(fixTasks) {
|
|
137
|
-
// Union-Find
|
|
138
|
-
const parent = fixTasks.map((_, i) => i);
|
|
139
|
-
|
|
140
|
-
function find(i) {
|
|
141
|
-
while (parent[i] !== i) {
|
|
142
|
-
parent[i] = parent[parent[i]];
|
|
143
|
-
i = parent[i];
|
|
144
|
-
}
|
|
145
|
-
return i;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function union(i, j) {
|
|
149
|
-
const pi = find(i), pj = find(j);
|
|
150
|
-
if (pi !== pj) parent[pi] = pj;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Merge tasks that share 2+ domain tags
|
|
154
|
-
for (let i = 0; i < fixTasks.length; i++) {
|
|
155
|
-
const dA = new Set(domainTags(fixTasks[i].tags));
|
|
156
|
-
for (let j = i + 1; j < fixTasks.length; j++) {
|
|
157
|
-
const dB = domainTags(fixTasks[j].tags);
|
|
158
|
-
const overlap = dB.filter(t => dA.has(t));
|
|
159
|
-
if (overlap.length >= 2) {
|
|
160
|
-
union(i, j);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Group by root
|
|
166
|
-
const clusters = new Map();
|
|
167
|
-
for (let i = 0; i < fixTasks.length; i++) {
|
|
168
|
-
const root = find(i);
|
|
169
|
-
if (!clusters.has(root)) clusters.set(root, []);
|
|
170
|
-
clusters.get(root).push(fixTasks[i]);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return Array.from(clusters.values());
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ─── Convert cluster → proposed anchor ────────────────────────────────────────
|
|
177
|
-
function clusterToAnchor(tasks) {
|
|
178
|
-
// Count domain tag frequency across cluster
|
|
179
|
-
const tagFreq = {};
|
|
180
|
-
for (const task of tasks) {
|
|
181
|
-
for (const tag of domainTags(task.tags)) {
|
|
182
|
-
tagFreq[tag] = (tagFreq[tag] || 0) + 1;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// source_tags: domain tags appearing in >= half the cluster tasks, sorted by freq desc
|
|
187
|
-
const threshold = Math.max(1, Math.floor(tasks.length / 2));
|
|
188
|
-
const sourceTags = Object.entries(tagFreq)
|
|
189
|
-
.filter(([, count]) => count >= threshold)
|
|
190
|
-
.sort((a, b) => b[1] - a[1])
|
|
191
|
-
.map(([tag]) => tag);
|
|
192
|
-
|
|
193
|
-
// concept: top 3 source_tags joined, or fallback to all top tags
|
|
194
|
-
const topTags = Object.entries(tagFreq)
|
|
195
|
-
.sort((a, b) => b[1] - a[1])
|
|
196
|
-
.slice(0, 3)
|
|
197
|
-
.map(([tag]) => tag);
|
|
198
|
-
const concept = topTags.join(' / ');
|
|
199
|
-
|
|
200
|
-
// Most recent task date
|
|
201
|
-
const dates = tasks.map(t => t.date).filter(Boolean).sort();
|
|
202
|
-
const lastTouched = dates.length ? dates[dates.length - 1] : new Date().toISOString().slice(0, 10);
|
|
203
|
-
|
|
204
|
-
const fixCycleCount = tasks.length;
|
|
205
|
-
const incompleteness = Math.min(fixCycleCount * 0.15, 0.9);
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
concept,
|
|
209
|
-
source_tags: sourceTags.length ? sourceTags : topTags,
|
|
210
|
-
incompleteness: Math.round(incompleteness * 100) / 100,
|
|
211
|
-
fix_cycle_count: fixCycleCount,
|
|
212
|
-
last_touched: lastTouched,
|
|
213
|
-
decay_halflife_days: 45,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
218
|
-
function main() {
|
|
219
|
-
const { projectPath, write } = parseArgs();
|
|
220
|
-
|
|
221
|
-
const doneDir = path.join(projectPath, '.kenaz', 'tasks', 'done');
|
|
222
|
-
const statePath = path.join(projectPath, '.kenaz', 'specialist', 'ansuz_motivational_state.json');
|
|
223
|
-
|
|
224
|
-
// Scan FIX tasks
|
|
225
|
-
const fixTasks = scanFixTasks(doneDir);
|
|
226
|
-
|
|
227
|
-
if (fixTasks.length === 0) {
|
|
228
|
-
process.stderr.write(`[bootstrap-anchors] No FIX tasks found in ${doneDir}\n`);
|
|
229
|
-
process.stdout.write(JSON.stringify([], null, 2) + '\n');
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
process.stderr.write(`[bootstrap-anchors] Found ${fixTasks.length} FIX task(s) in ${doneDir}\n`);
|
|
234
|
-
|
|
235
|
-
// Build clusters
|
|
236
|
-
const clusters = buildClusters(fixTasks);
|
|
237
|
-
process.stderr.write(`[bootstrap-anchors] Formed ${clusters.length} cluster(s)\n`);
|
|
238
|
-
|
|
239
|
-
// Convert to proposed anchors
|
|
240
|
-
const proposed = clusters.map(clusterToAnchor);
|
|
241
|
-
|
|
242
|
-
if (!write) {
|
|
243
|
-
// Read-only mode: print proposed anchors as JSON
|
|
244
|
-
process.stdout.write(JSON.stringify(proposed, null, 2) + '\n');
|
|
245
|
-
process.stderr.write('\n[bootstrap-anchors] Review the proposed anchors above.\n');
|
|
246
|
-
process.stderr.write('[bootstrap-anchors] To merge into ansuz_motivational_state.json, run with --write\n');
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// --write mode: merge proposed anchors into existing state
|
|
251
|
-
let state;
|
|
252
|
-
try {
|
|
253
|
-
state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
254
|
-
} catch {
|
|
255
|
-
process.stderr.write(`[bootstrap-anchors] ERROR: could not read ${statePath}\n`);
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const existing = Array.isArray(state.anchors) ? state.anchors : [];
|
|
260
|
-
let added = 0;
|
|
261
|
-
|
|
262
|
-
for (const anchor of proposed) {
|
|
263
|
-
// Skip if existing anchor already covers these source_tags (any overlap)
|
|
264
|
-
const alreadyCovered = existing.some(ex =>
|
|
265
|
-
Array.isArray(ex.source_tags) &&
|
|
266
|
-
ex.source_tags.some(t => anchor.source_tags.includes(t))
|
|
267
|
-
);
|
|
268
|
-
if (alreadyCovered) {
|
|
269
|
-
process.stderr.write(`[bootstrap-anchors] SKIP (covered): ${anchor.concept}\n`);
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
existing.push(anchor);
|
|
273
|
-
added++;
|
|
274
|
-
process.stderr.write(`[bootstrap-anchors] ADD: ${anchor.concept}\n`);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (added > 0) {
|
|
278
|
-
state.anchors = existing;
|
|
279
|
-
state.last_updated = new Date().toISOString();
|
|
280
|
-
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
281
|
-
process.stderr.write(`[bootstrap-anchors] Wrote ${added} new anchor(s) to ${statePath}\n`);
|
|
282
|
-
} else {
|
|
283
|
-
process.stderr.write('[bootstrap-anchors] No new anchors to add (all clusters already covered by existing anchors)\n');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
process.stdout.write(JSON.stringify(existing, null, 2) + '\n');
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
main();
|