arkaos 4.21.0 → 4.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THE-ARKAOS-GUIDE.md +72 -0
- package/VERSION +1 -1
- package/arka/SKILL.md +30 -7
- package/arka/skills/bootstrap-agent/SKILL.md +3 -2
- package/arka/skills/flow/SKILL.md +3 -2
- package/arka/skills/forge/SKILL.md +3 -2
- package/arka/skills/recipes/SKILL.md +3 -2
- package/arka/skills/refine/SKILL.md +3 -2
- package/arka/skills/research/SKILL.md +3 -2
- package/bin/arka-doctor +8 -0
- package/config/claude-agents/marta-cqo.md +9 -0
- package/config/standards/{claude-md-overlays → stack-rules}/laravel.md +11 -0
- package/config/standards/{claude-md-overlays → stack-rules}/node.md +10 -1
- package/config/standards/{claude-md-overlays → stack-rules}/nuxt.md +12 -1
- package/config/standards/stack-rules/php.md +13 -0
- package/config/standards/{claude-md-overlays → stack-rules}/python.md +5 -0
- package/config/standards/stack-rules/react.md +14 -0
- package/config/standards/stack-rules/vue.md +13 -0
- package/core/cognition/evolve.py +299 -0
- package/core/cognition/evolve_cli.py +55 -0
- package/core/cognition/insights/store.py +15 -4
- package/core/forge/persistence.py +27 -9
- package/core/forge/schema.py +45 -22
- package/core/governance/evidence_checks.py +101 -9
- package/core/governance/kb_cite_check.py +16 -6
- package/core/governance/stop_lint.py +293 -0
- package/core/governance/stop_lint_telemetry.py +141 -0
- package/core/governance/tool_loop_check.py +111 -0
- package/core/hooks/stop.py +73 -22
- package/core/hooks/user_prompt_submit.py +21 -23
- package/core/runtime/pricing.py +53 -19
- package/core/synapse/engine.py +46 -16
- package/core/synapse/graph_context_layer.py +7 -2
- package/core/synapse/kb_cache.py +63 -16
- package/core/synapse/layers.py +131 -495
- package/core/synapse/layers_base.py +115 -0
- package/core/synapse/layers_kb.py +701 -0
- package/core/sync/content_syncer.py +79 -10
- package/dashboard/app/components/AgentDependencyGraph.vue +5 -5
- package/dashboard/app/components/AgentEditDrawer.vue +120 -52
- package/dashboard/app/components/AgentSuggestionsCard.vue +4 -2
- package/dashboard/app/components/ConfirmDialog.vue +2 -2
- package/dashboard/app/components/DashboardState.vue +5 -3
- package/dashboard/app/components/GlobalSearch.vue +11 -7
- package/dashboard/app/components/KeyboardShortcutsHelp.vue +8 -6
- package/dashboard/app/components/KnowledgeSourcesList.vue +13 -9
- package/dashboard/app/components/MarkdownEditor.vue +1 -1
- package/dashboard/app/components/NotificationsBell.vue +8 -4
- package/dashboard/app/components/OnboardingTour.vue +24 -11
- package/dashboard/app/components/PersonaCloneDialog.vue +15 -8
- package/dashboard/app/components/PersonaWizard.vue +105 -72
- package/dashboard/app/components/SidebarFavoritesWidget.vue +5 -5
- package/dashboard/app/components/SidebarStatsWidget.vue +3 -1
- package/dashboard/app/components/Terminal.vue +9 -8
- package/dashboard/app/components/TextDiff.vue +27 -15
- package/dashboard/app/composables/useActivityFeed.ts +3 -3
- package/dashboard/app/composables/useApi.ts +4 -2
- package/dashboard/app/composables/useConfirmDialog.ts +1 -1
- package/dashboard/app/composables/useFavorites.ts +8 -8
- package/dashboard/app/composables/useTerminalThemes.ts +13 -13
- package/dashboard/app/composables/useThemeColor.ts +7 -4
- package/dashboard/app/layouts/default.vue +7 -0
- package/dashboard/app/pages/agents/[id].vue +319 -159
- package/dashboard/app/pages/agents/compare.vue +152 -52
- package/dashboard/app/pages/agents/index.vue +68 -55
- package/dashboard/app/pages/agents/new.vue +125 -58
- package/dashboard/app/pages/audit.vue +13 -6
- package/dashboard/app/pages/budget.vue +20 -18
- package/dashboard/app/pages/commands.vue +26 -22
- package/dashboard/app/pages/departments/[dept].vue +67 -31
- package/dashboard/app/pages/departments/compare.vue +85 -27
- package/dashboard/app/pages/departments/index.vue +31 -7
- package/dashboard/app/pages/health.vue +19 -12
- package/dashboard/app/pages/knowledge/index.vue +190 -78
- package/dashboard/app/pages/models.vue +74 -28
- package/dashboard/app/pages/personas/[id].vue +322 -219
- package/dashboard/app/pages/personas/archetypes.vue +20 -6
- package/dashboard/app/pages/personas/compare-with-agent.vue +109 -39
- package/dashboard/app/pages/personas/compare.vue +196 -124
- package/dashboard/app/pages/personas/index.vue +67 -60
- package/dashboard/app/pages/personas/new.vue +1 -1
- package/dashboard/app/pages/plan-canvas.vue +630 -0
- package/dashboard/app/pages/settings.vue +1 -0
- package/dashboard/app/pages/tasks.vue +80 -38
- package/dashboard/app/pages/trash.vue +11 -9
- package/dashboard/app/pages/workflows.vue +29 -19
- package/dashboard/nuxt.config.ts +2 -1
- package/dashboard/package.json +5 -0
- package/departments/brand/SKILL.md +3 -2
- package/departments/brand/skills/archetype-finder/SKILL.md +3 -2
- package/departments/brand/skills/colors/SKILL.md +3 -2
- package/departments/brand/skills/design-system/SKILL.md +3 -2
- package/departments/brand/skills/identity-system/SKILL.md +3 -2
- package/departments/brand/skills/logo-brief/SKILL.md +3 -2
- package/departments/brand/skills/mockup-generate/SKILL.md +3 -2
- package/departments/brand/skills/naming-evaluate/SKILL.md +3 -2
- package/departments/brand/skills/positioning-statement/SKILL.md +3 -2
- package/departments/brand/skills/primal-audit/SKILL.md +3 -2
- package/departments/brand/skills/ux-audit/SKILL.md +3 -2
- package/departments/brand/skills/voice-guide/SKILL.md +3 -2
- package/departments/brand/skills/wireframe/SKILL.md +3 -2
- package/departments/community/SKILL.md +3 -2
- package/departments/community/skills/ai-community/SKILL.md +3 -2
- package/departments/community/skills/betting-setup/SKILL.md +3 -2
- package/departments/community/skills/business-model/SKILL.md +3 -2
- package/departments/community/skills/content-calendar/SKILL.md +3 -2
- package/departments/community/skills/events-plan/SKILL.md +3 -2
- package/departments/community/skills/gamification-design/SKILL.md +3 -2
- package/departments/community/skills/growth-plan/SKILL.md +3 -2
- package/departments/community/skills/metrics-track/SKILL.md +3 -2
- package/departments/community/skills/moderation/SKILL.md +3 -2
- package/departments/community/skills/monetize-plan/SKILL.md +3 -2
- package/departments/community/skills/niche-setup/SKILL.md +3 -2
- package/departments/community/skills/onboarding-flow/SKILL.md +3 -2
- package/departments/community/skills/platform-select/SKILL.md +3 -2
- package/departments/content/SKILL.md +3 -2
- package/departments/content/skills/ai-workflow/SKILL.md +3 -2
- package/departments/content/skills/analytics/SKILL.md +3 -2
- package/departments/content/skills/calendar/SKILL.md +3 -2
- package/departments/content/skills/content-system/SKILL.md +3 -2
- package/departments/content/skills/monetization-plan/SKILL.md +3 -2
- package/departments/content/skills/newsletter-write/SKILL.md +3 -2
- package/departments/content/skills/platform-optimize/SKILL.md +3 -2
- package/departments/content/skills/repurpose-plan/SKILL.md +3 -2
- package/departments/content/skills/research-compile/SKILL.md +3 -2
- package/departments/content/skills/script-structure/SKILL.md +3 -2
- package/departments/content/skills/short-form/SKILL.md +3 -2
- package/departments/content/skills/thumbnail-package/SKILL.md +3 -2
- package/departments/content/skills/trend-hunt/SKILL.md +3 -2
- package/departments/content/skills/video-produce/SKILL.md +3 -2
- package/departments/content/skills/video-setup/SKILL.md +3 -2
- package/departments/content/skills/viral-design/SKILL.md +3 -2
- package/departments/content/skills/youtube-strategy/SKILL.md +3 -2
- package/departments/dev/SKILL.md +3 -2
- package/departments/dev/skills/ai-assisted-dev/SKILL.md +3 -2
- package/departments/dev/skills/animated-website/SKILL.md +7 -0
- package/departments/dev/skills/architecture-design/SKILL.md +3 -2
- package/departments/dev/skills/code-review/SKILL.md +3 -2
- package/departments/dev/skills/db-design/SKILL.md +3 -2
- package/departments/dev/skills/ddd-model/SKILL.md +3 -2
- package/departments/dev/skills/demo-gif/SKILL.md +3 -2
- package/departments/dev/skills/deploy/SKILL.md +3 -2
- package/departments/dev/skills/devops-pipeline/SKILL.md +3 -2
- package/departments/dev/skills/docs/SKILL.md +3 -2
- package/departments/dev/skills/mcp/SKILL.md +3 -2
- package/departments/dev/skills/performance-audit/SKILL.md +3 -2
- package/departments/dev/skills/refactor-plan/SKILL.md +3 -2
- package/departments/dev/skills/research/SKILL.md +3 -2
- package/departments/dev/skills/security-compliance/SKILL.md +3 -2
- package/departments/dev/skills/stack-check/SKILL.md +3 -2
- package/departments/ecom/SKILL.md +3 -2
- package/departments/ecom/skills/analytics/SKILL.md +3 -2
- package/departments/ecom/skills/browse-competitor/SKILL.md +3 -2
- package/departments/ecom/skills/cart-recovery/SKILL.md +3 -2
- package/departments/ecom/skills/cro-optimize/SKILL.md +3 -2
- package/departments/ecom/skills/customer-journey/SKILL.md +3 -2
- package/departments/ecom/skills/fulfillment-plan/SKILL.md +3 -2
- package/departments/ecom/skills/marketplace-manage/SKILL.md +3 -2
- package/departments/ecom/skills/pricing-strategy/SKILL.md +3 -2
- package/departments/ecom/skills/product-launch/SKILL.md +3 -2
- package/departments/ecom/skills/rfm-segment/SKILL.md +3 -2
- package/departments/ecom/skills/social-commerce/SKILL.md +3 -2
- package/departments/ecom/skills/store-audit/SKILL.md +3 -2
- package/departments/ecom/skills/subscription-model/SKILL.md +3 -2
- package/departments/finance/SKILL.md +3 -2
- package/departments/finance/skills/budget-plan/SKILL.md +3 -2
- package/departments/finance/skills/cashflow-forecast/SKILL.md +3 -2
- package/departments/finance/skills/ciso-advisor/SKILL.md +3 -2
- package/departments/finance/skills/financial-model/SKILL.md +3 -2
- package/departments/finance/skills/pitch-deck/SKILL.md +3 -2
- package/departments/finance/skills/scenario-analysis/SKILL.md +3 -2
- package/departments/finance/skills/unit-economics/SKILL.md +3 -2
- package/departments/finance/skills/valuation-model/SKILL.md +3 -2
- package/departments/kb/SKILL.md +3 -2
- package/departments/kb/skills/ai-research/SKILL.md +3 -2
- package/departments/kb/skills/competitive-intel/SKILL.md +3 -2
- package/departments/kb/skills/doc-extraction/SKILL.md +100 -0
- package/departments/kb/skills/doc-redaction/SKILL.md +104 -0
- package/departments/kb/skills/knowledge-ops/SKILL.md +3 -2
- package/departments/kb/skills/knowledge-review/SKILL.md +3 -2
- package/departments/kb/skills/learn-content/SKILL.md +3 -2
- package/departments/kb/skills/moc-create/SKILL.md +3 -2
- package/departments/kb/skills/persona-build/SKILL.md +3 -2
- package/departments/kb/skills/research-plan/SKILL.md +3 -2
- package/departments/kb/skills/search-kb/SKILL.md +3 -2
- package/departments/kb/skills/source-evaluate/SKILL.md +3 -2
- package/departments/kb/skills/taxonomy-manage/SKILL.md +3 -2
- package/departments/kb/skills/write-as-persona/SKILL.md +3 -2
- package/departments/landing/SKILL.md +3 -2
- package/departments/landing/skills/ab-test/SKILL.md +3 -2
- package/departments/landing/skills/affiliate-bridge/SKILL.md +3 -2
- package/departments/landing/skills/awareness-diagnose/SKILL.md +3 -2
- package/departments/landing/skills/email-sequence/SKILL.md +3 -2
- package/departments/landing/skills/funnel-metrics/SKILL.md +3 -2
- package/departments/landing/skills/headline-write/SKILL.md +3 -2
- package/departments/landing/skills/launch-sequence/SKILL.md +3 -2
- package/departments/landing/skills/offer-create/SKILL.md +3 -2
- package/departments/landing/skills/optimize-page/SKILL.md +3 -2
- package/departments/landing/skills/page-architect/SKILL.md +3 -2
- package/departments/landing/skills/persuasion-apply/SKILL.md +3 -2
- package/departments/landing/skills/webinar-funnel/SKILL.md +3 -2
- package/departments/leadership/SKILL.md +3 -2
- package/departments/leadership/skills/change-manage/SKILL.md +3 -2
- package/departments/leadership/skills/conflict-resolve/SKILL.md +3 -2
- package/departments/leadership/skills/culture-audit/SKILL.md +3 -2
- package/departments/leadership/skills/delegation-matrix/SKILL.md +3 -2
- package/departments/leadership/skills/disc-assess/SKILL.md +3 -2
- package/departments/leadership/skills/feedback-give/SKILL.md +3 -2
- package/departments/leadership/skills/hiring-plan/SKILL.md +3 -2
- package/departments/leadership/skills/performance-review/SKILL.md +3 -2
- package/departments/marketing/SKILL.md +3 -2
- package/departments/marketing/skills/ab-test/SKILL.md +3 -2
- package/departments/marketing/skills/analytics-report/SKILL.md +3 -2
- package/departments/marketing/skills/audience-segment/SKILL.md +3 -2
- package/departments/marketing/skills/calendar-plan/SKILL.md +3 -2
- package/departments/marketing/skills/competitor-analysis/SKILL.md +3 -2
- package/departments/marketing/skills/content-audit/SKILL.md +3 -2
- package/departments/marketing/skills/email-sequence/SKILL.md +3 -2
- package/departments/marketing/skills/growth-loop/SKILL.md +3 -2
- package/departments/marketing/skills/marketing-automation/SKILL.md +3 -2
- package/departments/marketing/skills/paid-campaign/SKILL.md +3 -2
- package/departments/marketing/skills/programmatic-seo/SKILL.md +3 -2
- package/departments/marketing/skills/seo-audit/SKILL.md +3 -2
- package/departments/marketing/skills/social-strategy/SKILL.md +3 -2
- package/departments/ops/SKILL.md +3 -2
- package/departments/ops/skills/bottleneck-find/SKILL.md +3 -2
- package/departments/ops/skills/dashboard-build/SKILL.md +3 -2
- package/departments/ops/skills/gdpr-compliance/SKILL.md +3 -2
- package/departments/ops/skills/github-ops/SKILL.md +3 -2
- package/departments/ops/skills/gtd-setup/SKILL.md +3 -2
- package/departments/ops/skills/integration-design/SKILL.md +3 -2
- package/departments/ops/skills/iso27001/SKILL.md +3 -2
- package/departments/ops/skills/lean-audit/SKILL.md +3 -2
- package/departments/ops/skills/metrics-dashboard/SKILL.md +3 -2
- package/departments/ops/skills/n8n-flow/SKILL.md +3 -2
- package/departments/ops/skills/quality-management/SKILL.md +3 -2
- package/departments/ops/skills/risk-management/SKILL.md +3 -2
- package/departments/ops/skills/soc2-compliance/SKILL.md +3 -2
- package/departments/ops/skills/sop-create/SKILL.md +3 -2
- package/departments/ops/skills/terminal-ops/SKILL.md +3 -2
- package/departments/ops/skills/workflow-automate/SKILL.md +3 -2
- package/departments/ops/skills/zapier-flow/SKILL.md +3 -2
- package/departments/org/SKILL.md +3 -2
- package/departments/org/skills/compensation-plan/SKILL.md +3 -2
- package/departments/org/skills/culture-define/SKILL.md +3 -2
- package/departments/org/skills/decision-framework/SKILL.md +3 -2
- package/departments/org/skills/hiring-plan/SKILL.md +3 -2
- package/departments/org/skills/meeting-optimize/SKILL.md +3 -2
- package/departments/org/skills/onboarding-design/SKILL.md +3 -2
- package/departments/org/skills/org-design/SKILL.md +3 -2
- package/departments/org/skills/remote-setup/SKILL.md +3 -2
- package/departments/org/skills/sop-process/SKILL.md +3 -2
- package/departments/org/skills/team-assess/SKILL.md +3 -2
- package/departments/pm/SKILL.md +3 -2
- package/departments/pm/skills/backlog-groom/SKILL.md +3 -2
- package/departments/pm/skills/discovery-plan/SKILL.md +3 -2
- package/departments/pm/skills/estimate-forecast/SKILL.md +3 -2
- package/departments/pm/skills/impact-map/SKILL.md +3 -2
- package/departments/pm/skills/kanban-setup/SKILL.md +3 -2
- package/departments/pm/skills/risk-register/SKILL.md +3 -2
- package/departments/pm/skills/roadmap-build/SKILL.md +3 -2
- package/departments/pm/skills/sprint-plan/SKILL.md +3 -2
- package/departments/pm/skills/stakeholder-map/SKILL.md +3 -2
- package/departments/pm/skills/standup-run/SKILL.md +3 -2
- package/departments/pm/skills/story-write/SKILL.md +3 -2
- package/departments/saas/SKILL.md +3 -2
- package/departments/saas/skills/benchmark-compare/SKILL.md +3 -2
- package/departments/saas/skills/churn-analysis/SKILL.md +3 -2
- package/departments/saas/skills/customer-success/SKILL.md +3 -2
- package/departments/saas/skills/growth-plan/SKILL.md +3 -2
- package/departments/saas/skills/gtm-strategy/SKILL.md +3 -2
- package/departments/saas/skills/launch-execute/SKILL.md +3 -2
- package/departments/saas/skills/metrics-dashboard/SKILL.md +3 -2
- package/departments/saas/skills/micro-saas-stack/SKILL.md +3 -2
- package/departments/saas/skills/mvp-build/SKILL.md +3 -2
- package/departments/saas/skills/niche-evaluate/SKILL.md +3 -2
- package/departments/saas/skills/onboarding-optimize/SKILL.md +3 -2
- package/departments/saas/skills/plg-setup/SKILL.md +3 -2
- package/departments/saas/skills/pricing-strategy/SKILL.md +3 -2
- package/departments/saas/skills/validate-idea/SKILL.md +3 -2
- package/departments/sales/SKILL.md +3 -2
- package/departments/sales/skills/challenger-sell/SKILL.md +3 -2
- package/departments/sales/skills/deal-qualify/SKILL.md +3 -2
- package/departments/sales/skills/discovery-call/SKILL.md +3 -2
- package/departments/sales/skills/forecast-revenue/SKILL.md +3 -2
- package/departments/sales/skills/negotiate-plan/SKILL.md +3 -2
- package/departments/sales/skills/objection-handle/SKILL.md +3 -2
- package/departments/sales/skills/pipeline-manage/SKILL.md +3 -2
- package/departments/sales/skills/pricing-negotiate/SKILL.md +3 -2
- package/departments/strategy/SKILL.md +3 -2
- package/departments/strategy/skills/blue-ocean/SKILL.md +3 -2
- package/departments/strategy/skills/bmc/SKILL.md +3 -2
- package/departments/strategy/skills/board-advisor/SKILL.md +3 -2
- package/departments/strategy/skills/cto-advisor/SKILL.md +3 -2
- package/departments/strategy/skills/extract-data/SKILL.md +3 -2
- package/departments/strategy/skills/five-forces/SKILL.md +3 -2
- package/departments/strategy/skills/growth-strategy/SKILL.md +3 -2
- package/departments/strategy/skills/moat-analysis/SKILL.md +3 -2
- package/departments/strategy/skills/position/SKILL.md +3 -2
- package/departments/strategy/skills/scenario-plan/SKILL.md +3 -2
- package/installer/config-seed.js +81 -25
- package/installer/graphify.js +340 -0
- package/installer/index.js +9 -2
- package/installer/keys.js +18 -3
- package/installer/update.js +8 -1
- package/knowledge/commands-registry.json +16 -3
- package/knowledge/skills-manifest.json +27 -1
- package/mcps/registry.json +9 -9
- package/mcps/scripts/apply-mcps.sh +6 -2
- package/package.json +2 -1
- package/pyproject.toml +1 -1
- package/scripts/dashboard-api.py +336 -124
- package/scripts/guide_gen.py +139 -0
- package/scripts/migrate_skills_kb_first.py +3 -2
- package/scripts/synapse-bridge.py +9 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Evolve — turn accumulated cross-project signals into instinct proposals.
|
|
2
|
+
|
|
3
|
+
Propose-only, LLM-free. The engine ingests the raw signals sessions
|
|
4
|
+
already accumulate (``~/.arkaos/gotchas.json`` — error patterns with
|
|
5
|
+
occurrence counts and the projects they appeared in) into the
|
|
6
|
+
``InsightStore`` as deterministic instincts: confidence derives from the
|
|
7
|
+
occurrence count inside the [0.3, 0.9] band, evidence_count mirrors the
|
|
8
|
+
raw count, and the record id is a stable digest of (source, title,
|
|
9
|
+
project) so re-ingestion is idempotent. It then renders a markdown
|
|
10
|
+
proposal at ``~/.arkaos/evolve-proposals/<date>.md`` with the
|
|
11
|
+
cross-project promotion candidates (``InsightStore.promotable`` — this
|
|
12
|
+
module is that API's first production caller) and the strongest
|
|
13
|
+
instincts.
|
|
14
|
+
|
|
15
|
+
Boundaries: Dreaming owns vault clustering (LLM, multi-backend) — evolve
|
|
16
|
+
never re-clusters markdown; it consumes structured stores only. Nothing
|
|
17
|
+
is promoted, modified, or deleted: the proposal file is the only output
|
|
18
|
+
(the reorganizer contract). Client identifiers are redacted and
|
|
19
|
+
project names are never rendered — only distinct-project counts.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import hashlib
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import tempfile
|
|
28
|
+
from dataclasses import dataclass, field
|
|
29
|
+
from datetime import UTC, datetime
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from core.cognition.insights.store import InsightStore
|
|
33
|
+
from core.cognition.memory.schemas import (
|
|
34
|
+
INSTINCT_CONFIDENCE_MAX,
|
|
35
|
+
INSTINCT_CONFIDENCE_MIN,
|
|
36
|
+
ActionableInsight,
|
|
37
|
+
)
|
|
38
|
+
from core.cognition.reorganizer import md_escape, redact_clients
|
|
39
|
+
|
|
40
|
+
_DEFAULT_OUTPUT_DIR = Path.home() / ".arkaos" / "evolve-proposals"
|
|
41
|
+
_DEFAULT_GOTCHAS_PATH = Path.home() / ".arkaos" / "gotchas.json"
|
|
42
|
+
_DEFAULT_DB_PATH = Path.home() / ".arkaos" / "insights.db"
|
|
43
|
+
_PROPOSAL_FILENAME_FMT = "%Y-%m-%d.md"
|
|
44
|
+
_CONFIDENCE_STEP = 0.1
|
|
45
|
+
_TOP_INSTINCTS = 10
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class PromotionCandidate:
|
|
50
|
+
"""Cross-project instinct eligible for promotion (propose-only)."""
|
|
51
|
+
|
|
52
|
+
title: str
|
|
53
|
+
project_count: int
|
|
54
|
+
mean_confidence: float
|
|
55
|
+
evidence_count: int
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class EvolveReport:
|
|
60
|
+
"""Evolve run outcome. Never contains client or project identifiers."""
|
|
61
|
+
|
|
62
|
+
date: str
|
|
63
|
+
ingested: int
|
|
64
|
+
pending_instincts: int
|
|
65
|
+
candidates: list[PromotionCandidate] = field(default_factory=list)
|
|
66
|
+
proposal_path: str | None = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def derive_confidence(count: int) -> float:
|
|
70
|
+
"""Occurrence count -> confidence, deterministic, inside the band."""
|
|
71
|
+
raw = INSTINCT_CONFIDENCE_MIN + _CONFIDENCE_STEP * (max(1, count) - 1)
|
|
72
|
+
return min(INSTINCT_CONFIDENCE_MAX, raw)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _stable_id(source: str, title: str, project: str) -> str:
|
|
76
|
+
digest = hashlib.sha1(f"{source}:{title}:{project}".encode())
|
|
77
|
+
return digest.hexdigest()[:32]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _load_gotchas(gotchas_path: Path) -> list[dict]:
|
|
81
|
+
try:
|
|
82
|
+
data = json.loads(gotchas_path.read_text(encoding="utf-8"))
|
|
83
|
+
except (OSError, json.JSONDecodeError):
|
|
84
|
+
return []
|
|
85
|
+
entries = data.get("gotchas") if isinstance(data, dict) else data
|
|
86
|
+
return [e for e in entries or [] if isinstance(e, dict)]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _instinct_from_gotcha(gotcha: dict, project: str) -> ActionableInsight | None:
|
|
90
|
+
title = str(gotcha.get("pattern") or "").strip()
|
|
91
|
+
if not title or not project.strip():
|
|
92
|
+
return None
|
|
93
|
+
count = int(gotcha.get("count") or 1)
|
|
94
|
+
return ActionableInsight(
|
|
95
|
+
id=_stable_id("gotcha", title, project),
|
|
96
|
+
project=project,
|
|
97
|
+
trigger="evolve-ingest",
|
|
98
|
+
category="technical",
|
|
99
|
+
severity="improve" if count >= 5 else "consider",
|
|
100
|
+
title=title,
|
|
101
|
+
description=str(gotcha.get("full_pattern") or title),
|
|
102
|
+
recommendation=str(gotcha.get("suggestion") or "").strip()
|
|
103
|
+
or "Recurring signal — consider a rule, skill, or fix.",
|
|
104
|
+
context=f"tool={gotcha.get('tool', '?')} count={count}",
|
|
105
|
+
confidence=derive_confidence(count),
|
|
106
|
+
evidence_count=max(1, count),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def ingest_gotchas(store: InsightStore, gotchas_path: Path) -> int:
|
|
111
|
+
"""Mirror gotcha signals into the store as project-scoped instincts.
|
|
112
|
+
|
|
113
|
+
Deterministic derivation from the source of truth: re-ingesting
|
|
114
|
+
recomputes the same ids and values (INSERT OR REPLACE), so repeat
|
|
115
|
+
runs are idempotent.
|
|
116
|
+
"""
|
|
117
|
+
ingested = 0
|
|
118
|
+
for gotcha in _load_gotchas(gotchas_path):
|
|
119
|
+
projects = gotcha.get("projects") or []
|
|
120
|
+
for project in {str(p) for p in projects if str(p).strip()}:
|
|
121
|
+
insight = _instinct_from_gotcha(gotcha, project)
|
|
122
|
+
if insight is None:
|
|
123
|
+
continue
|
|
124
|
+
store.save(insight)
|
|
125
|
+
ingested += 1
|
|
126
|
+
return ingested
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _candidates(
|
|
130
|
+
store: InsightStore, min_projects: int, min_confidence: float,
|
|
131
|
+
) -> list[PromotionCandidate]:
|
|
132
|
+
titles = store.promotable(
|
|
133
|
+
min_projects=min_projects, min_confidence=min_confidence
|
|
134
|
+
)
|
|
135
|
+
rows = [i for i in store.get_all_pending() if i.scope == "project"]
|
|
136
|
+
out: list[PromotionCandidate] = []
|
|
137
|
+
for title in titles:
|
|
138
|
+
group = [i for i in rows if i.title == title]
|
|
139
|
+
if not group:
|
|
140
|
+
continue
|
|
141
|
+
out.append(
|
|
142
|
+
PromotionCandidate(
|
|
143
|
+
title=title,
|
|
144
|
+
project_count=len({i.project for i in group}),
|
|
145
|
+
mean_confidence=round(
|
|
146
|
+
sum(i.confidence for i in group) / len(group), 2
|
|
147
|
+
),
|
|
148
|
+
evidence_count=sum(i.evidence_count for i in group),
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
return out
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def build_proposal(
|
|
155
|
+
*,
|
|
156
|
+
db_path: Path | None = None,
|
|
157
|
+
gotchas_path: Path | None = None,
|
|
158
|
+
output_dir: Path | None = None,
|
|
159
|
+
dry_run: bool = False,
|
|
160
|
+
min_projects: int = 2,
|
|
161
|
+
min_confidence: float = 0.8,
|
|
162
|
+
) -> EvolveReport:
|
|
163
|
+
"""Ingest signals, compute promotion candidates, write the proposal."""
|
|
164
|
+
store = InsightStore(db_path or _DEFAULT_DB_PATH)
|
|
165
|
+
ingested = ingest_gotchas(store, gotchas_path or _DEFAULT_GOTCHAS_PATH)
|
|
166
|
+
pending = store.get_all_pending()
|
|
167
|
+
candidates = _candidates(store, min_projects, min_confidence)
|
|
168
|
+
date = datetime.now(UTC).strftime("%Y-%m-%d")
|
|
169
|
+
markdown = _render(date, ingested, pending, candidates)
|
|
170
|
+
if dry_run:
|
|
171
|
+
print(markdown)
|
|
172
|
+
return EvolveReport(date, ingested, len(pending), candidates)
|
|
173
|
+
path = _write_report(markdown, output_dir)
|
|
174
|
+
return EvolveReport(date, ingested, len(pending), candidates, str(path))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _render_candidate(candidate: PromotionCandidate) -> str:
|
|
178
|
+
title = md_escape(redact_clients(candidate.title))
|
|
179
|
+
return (
|
|
180
|
+
f"| {title} | {candidate.project_count} | "
|
|
181
|
+
f"{candidate.mean_confidence:.2f} | {candidate.evidence_count} |"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _render_instinct(insight: ActionableInsight) -> str:
|
|
186
|
+
title = md_escape(redact_clients(insight.title))
|
|
187
|
+
rec = md_escape(redact_clients(insight.recommendation))
|
|
188
|
+
return (
|
|
189
|
+
f"- **{title}** — confidence {insight.confidence:.2f}, "
|
|
190
|
+
f"evidence {insight.evidence_count}. {rec}"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _render_summary(
|
|
195
|
+
date: str, ingested: int, pending_count: int, candidate_count: int,
|
|
196
|
+
) -> list[str]:
|
|
197
|
+
return [
|
|
198
|
+
f"# Evolve proposal — {date}",
|
|
199
|
+
"",
|
|
200
|
+
"> Propose-only: nothing was promoted or modified. Review and",
|
|
201
|
+
"> act via the squads; promotion stays a deliberate operator call.",
|
|
202
|
+
"",
|
|
203
|
+
"## Summary",
|
|
204
|
+
"",
|
|
205
|
+
f"- Instincts ingested this run: **{ingested}**",
|
|
206
|
+
f"- Pending instincts in store: **{pending_count}**",
|
|
207
|
+
f"- Promotion candidates: **{candidate_count}**",
|
|
208
|
+
"",
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _render_candidates_block(candidates: list[PromotionCandidate]) -> list[str]:
|
|
213
|
+
lines = ["## Promotion candidates (cross-project)", ""]
|
|
214
|
+
if candidates:
|
|
215
|
+
lines += [
|
|
216
|
+
"| Instinct | Projects | Mean confidence | Evidence |",
|
|
217
|
+
"|---|---|---|---|",
|
|
218
|
+
*[_render_candidate(c) for c in candidates],
|
|
219
|
+
]
|
|
220
|
+
else:
|
|
221
|
+
lines.append("None yet — needs the same signal in 2+ projects.")
|
|
222
|
+
return lines
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _strongest_by_title(pending: list[ActionableInsight]) -> list[ActionableInsight]:
|
|
226
|
+
"""Top instincts, one row per title (a cross-project pattern has one
|
|
227
|
+
project-scoped row per project — the operator needs it once)."""
|
|
228
|
+
ranked = sorted(
|
|
229
|
+
pending, key=lambda i: (i.confidence, i.evidence_count), reverse=True
|
|
230
|
+
)
|
|
231
|
+
seen: set[str] = set()
|
|
232
|
+
out: list[ActionableInsight] = []
|
|
233
|
+
for insight in ranked:
|
|
234
|
+
if insight.title in seen:
|
|
235
|
+
continue
|
|
236
|
+
seen.add(insight.title)
|
|
237
|
+
out.append(insight)
|
|
238
|
+
if len(out) == _TOP_INSTINCTS:
|
|
239
|
+
break
|
|
240
|
+
return out
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _render_instincts_block(pending: list[ActionableInsight]) -> list[str]:
|
|
244
|
+
strongest = _strongest_by_title(pending)
|
|
245
|
+
lines = ["", "## Strongest instincts", ""]
|
|
246
|
+
if strongest:
|
|
247
|
+
lines += [_render_instinct(i) for i in strongest]
|
|
248
|
+
else:
|
|
249
|
+
lines.append("Store is empty — no signals accumulated yet.")
|
|
250
|
+
return lines
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _render(
|
|
254
|
+
date: str,
|
|
255
|
+
ingested: int,
|
|
256
|
+
pending: list[ActionableInsight],
|
|
257
|
+
candidates: list[PromotionCandidate],
|
|
258
|
+
) -> str:
|
|
259
|
+
lines = (
|
|
260
|
+
_render_summary(date, ingested, len(pending), len(candidates))
|
|
261
|
+
+ _render_candidates_block(candidates)
|
|
262
|
+
+ _render_instincts_block(pending)
|
|
263
|
+
)
|
|
264
|
+
return "\n".join(lines) + "\n"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _write_report(markdown: str, output_dir: Path | None) -> Path:
|
|
268
|
+
"""Atomic markdown write to an allowlisted output directory."""
|
|
269
|
+
out = _validate_output_dir(output_dir)
|
|
270
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
271
|
+
report_path = out / datetime.now(UTC).strftime(
|
|
272
|
+
_PROPOSAL_FILENAME_FMT
|
|
273
|
+
)
|
|
274
|
+
tmp_path = report_path.with_suffix(f".tmp-{os.getpid()}.md")
|
|
275
|
+
tmp_path.write_text(markdown, encoding="utf-8")
|
|
276
|
+
os.replace(tmp_path, report_path)
|
|
277
|
+
return report_path
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _validate_output_dir(output_dir: Path | None) -> Path:
|
|
281
|
+
"""Allowlist writes to ~/.arkaos or the system tempdir (defence in
|
|
282
|
+
depth — same contract as the reorganizer)."""
|
|
283
|
+
if output_dir is None:
|
|
284
|
+
return _DEFAULT_OUTPUT_DIR
|
|
285
|
+
resolved = Path(output_dir).expanduser().resolve()
|
|
286
|
+
allowed_roots = (
|
|
287
|
+
(Path.home() / ".arkaos").resolve(),
|
|
288
|
+
Path(tempfile.gettempdir()).resolve(),
|
|
289
|
+
)
|
|
290
|
+
for root in allowed_roots:
|
|
291
|
+
try:
|
|
292
|
+
resolved.relative_to(root)
|
|
293
|
+
return resolved
|
|
294
|
+
except ValueError:
|
|
295
|
+
continue
|
|
296
|
+
raise ValueError(
|
|
297
|
+
"output_dir must be under one of "
|
|
298
|
+
f"{[str(r) for r in allowed_roots]}; got {resolved}"
|
|
299
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""CLI for the evolve engine: ``python -m core.cognition.evolve_cli``.
|
|
2
|
+
|
|
3
|
+
Mirrors ``reorganizer_cli``: argparse in, one ``build_proposal`` call,
|
|
4
|
+
human-readable summary out. ``--dry-run`` prints the proposal to stdout
|
|
5
|
+
without writing anything.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
|
|
12
|
+
from core.cognition.evolve import build_proposal
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
prog="python -m core.cognition.evolve_cli",
|
|
18
|
+
description=(
|
|
19
|
+
"Ingest accumulated signals into instincts and write a "
|
|
20
|
+
"propose-only evolution proposal."
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--min-projects", type=int, default=2,
|
|
25
|
+
help="distinct projects required for a promotion candidate",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--min-confidence", type=float, default=0.8,
|
|
29
|
+
help="mean confidence required for a promotion candidate",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--dry-run", action="store_true",
|
|
33
|
+
help="print the proposal to stdout; write nothing",
|
|
34
|
+
)
|
|
35
|
+
return parser
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main(argv: list[str] | None = None) -> int:
|
|
39
|
+
args = _build_parser().parse_args(argv)
|
|
40
|
+
report = build_proposal(
|
|
41
|
+
dry_run=args.dry_run,
|
|
42
|
+
min_projects=args.min_projects,
|
|
43
|
+
min_confidence=args.min_confidence,
|
|
44
|
+
)
|
|
45
|
+
if not args.dry_run:
|
|
46
|
+
print(
|
|
47
|
+
f"evolve: ingested={report.ingested} "
|
|
48
|
+
f"pending={report.pending_instincts} "
|
|
49
|
+
f"candidates={len(report.candidates)} -> {report.proposal_path}"
|
|
50
|
+
)
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
raise SystemExit(main())
|
|
@@ -4,7 +4,9 @@ Persists ActionableInsight instances with support for status-based retrieval,
|
|
|
4
4
|
project filtering, presentation lifecycle, and dismissal analytics.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import contextlib
|
|
7
8
|
import sqlite3
|
|
9
|
+
from collections.abc import Iterator
|
|
8
10
|
from datetime import UTC, datetime
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
|
|
@@ -24,11 +26,20 @@ class InsightStore:
|
|
|
24
26
|
self._db_path = db_path
|
|
25
27
|
self._init_db()
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
@contextlib.contextmanager
|
|
30
|
+
def _conn(self) -> Iterator[sqlite3.Connection]:
|
|
31
|
+
"""Per-operation connection: transaction semantics AND a real
|
|
32
|
+
close. ``with sqlite3.connect(...)`` alone only commits/rolls
|
|
33
|
+
back — it never closes, leaking one connection per operation
|
|
34
|
+
(ResourceWarning under coverage, amplified by ingest loops)."""
|
|
28
35
|
conn = sqlite3.connect(self._db_path)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
try:
|
|
37
|
+
conn.row_factory = sqlite3.Row
|
|
38
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
39
|
+
with conn:
|
|
40
|
+
yield conn
|
|
41
|
+
finally:
|
|
42
|
+
conn.close()
|
|
32
43
|
|
|
33
44
|
# Instinct columns added after the original 12-column schema. An
|
|
34
45
|
# operator's existing DB has the old shape, so these are ALTER-added
|
|
@@ -4,8 +4,9 @@ import os
|
|
|
4
4
|
import re as _re
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from tempfile import NamedTemporaryFile
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
import yaml
|
|
9
|
+
|
|
9
10
|
from core.forge.schema import ForgePlan, ForgeStatus
|
|
10
11
|
|
|
11
12
|
|
|
@@ -23,7 +24,9 @@ def save_plan(plan: ForgePlan) -> Path:
|
|
|
23
24
|
plans.mkdir(parents=True, exist_ok=True)
|
|
24
25
|
target = plans / f"{plan.id}.yaml"
|
|
25
26
|
data = plan.model_dump(mode="json")
|
|
26
|
-
fd = NamedTemporaryFile(
|
|
27
|
+
fd = NamedTemporaryFile( # noqa: SIM115 — handle outlives the block: os.replace consumes fd.name
|
|
28
|
+
mode="w", dir=str(plans), suffix=".tmp", delete=False, encoding="utf-8"
|
|
29
|
+
)
|
|
27
30
|
try:
|
|
28
31
|
yaml.dump(data, fd, default_flow_style=False, allow_unicode=True)
|
|
29
32
|
fd.close()
|
|
@@ -35,7 +38,7 @@ def save_plan(plan: ForgePlan) -> Path:
|
|
|
35
38
|
return target
|
|
36
39
|
|
|
37
40
|
|
|
38
|
-
def load_plan(plan_id: str) ->
|
|
41
|
+
def load_plan(plan_id: str) -> ForgePlan | None:
|
|
39
42
|
"""Load a forge plan by ID. Returns None if not found."""
|
|
40
43
|
path = _plans_dir() / f"{plan_id}.yaml"
|
|
41
44
|
if not path.exists():
|
|
@@ -54,12 +57,20 @@ def list_plans() -> list[dict]:
|
|
|
54
57
|
if path.name == "active.yaml":
|
|
55
58
|
continue
|
|
56
59
|
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
60
|
+
if not isinstance(data, dict):
|
|
61
|
+
continue
|
|
62
|
+
# Pre-v4 plans stored complexity/critic as scalars — a summary
|
|
63
|
+
# must tolerate every shape that ever hit disk.
|
|
64
|
+
complexity = data.get("complexity")
|
|
65
|
+
critic = data.get("critic")
|
|
57
66
|
results.append({
|
|
58
67
|
"id": data.get("id", path.stem),
|
|
59
68
|
"name": data.get("name", ""),
|
|
60
69
|
"status": data.get("status", "draft"),
|
|
61
|
-
"tier":
|
|
62
|
-
|
|
70
|
+
"tier": complexity.get("tier", "shallow")
|
|
71
|
+
if isinstance(complexity, dict) else "shallow",
|
|
72
|
+
"confidence": critic.get("confidence", 0.0)
|
|
73
|
+
if isinstance(critic, dict) else 0.0,
|
|
63
74
|
"created_at": data.get("created_at", ""),
|
|
64
75
|
})
|
|
65
76
|
return results
|
|
@@ -72,7 +83,7 @@ def set_active_plan(plan_id: str) -> None:
|
|
|
72
83
|
link.write_text(plan_id, encoding="utf-8")
|
|
73
84
|
|
|
74
85
|
|
|
75
|
-
def get_active_plan() ->
|
|
86
|
+
def get_active_plan() -> ForgePlan | None:
|
|
76
87
|
"""Get the currently active forge plan."""
|
|
77
88
|
link = _active_link()
|
|
78
89
|
if not link.exists():
|
|
@@ -94,7 +105,10 @@ def clear_active_plan() -> None:
|
|
|
94
105
|
|
|
95
106
|
def _obsidian_forge_dir() -> Path:
|
|
96
107
|
"""Obsidian vault path for Forge documents."""
|
|
97
|
-
return
|
|
108
|
+
return (
|
|
109
|
+
Path.home() / "Documents" / "Personal" / "Projects"
|
|
110
|
+
/ "WizardingCode Internal" / "ArkaOS" / "Forge"
|
|
111
|
+
)
|
|
98
112
|
|
|
99
113
|
|
|
100
114
|
def export_to_obsidian(plan: ForgePlan) -> Path:
|
|
@@ -150,7 +164,8 @@ def _render_obsidian_context(plan: ForgePlan) -> list[str]:
|
|
|
150
164
|
ctx = plan.context
|
|
151
165
|
return [
|
|
152
166
|
"## Context",
|
|
153
|
-
f"Repo: {ctx.repo} | Branch: {ctx.branch} |
|
|
167
|
+
f"Repo: {ctx.repo} | Branch: {ctx.branch} | "
|
|
168
|
+
f"Commit: {ctx.commit_at_forge} | ArkaOS: {ctx.arkaos_version}",
|
|
154
169
|
"",
|
|
155
170
|
"## Prompt",
|
|
156
171
|
f"> {ctx.prompt}",
|
|
@@ -195,7 +210,10 @@ def _render_obsidian_critic(plan: ForgePlan) -> list[str]:
|
|
|
195
210
|
if critic.risks:
|
|
196
211
|
lines.append("### Risks")
|
|
197
212
|
for risk in critic.risks:
|
|
198
|
-
lines.append(
|
|
213
|
+
lines.append(
|
|
214
|
+
f"- **{risk.risk}** ({risk.severity.value}) — "
|
|
215
|
+
f"Mitigation: {risk.mitigation}"
|
|
216
|
+
)
|
|
199
217
|
lines.append("")
|
|
200
218
|
return lines
|
|
201
219
|
|
package/core/forge/schema.py
CHANGED
|
@@ -6,23 +6,21 @@ structured ForgePlan that downstream agents consume.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from enum import Enum
|
|
9
|
-
from typing import List, Optional
|
|
10
9
|
|
|
11
10
|
from pydantic import BaseModel, Field, field_validator
|
|
12
11
|
|
|
13
|
-
|
|
14
12
|
# ---------------------------------------------------------------------------
|
|
15
13
|
# Enums
|
|
16
14
|
# ---------------------------------------------------------------------------
|
|
17
15
|
|
|
18
|
-
class ForgeTier(str, Enum):
|
|
16
|
+
class ForgeTier(str, Enum): # noqa: UP042 — StrEnum flips str()/format(); migration is its own change
|
|
19
17
|
"""Execution tier determined by complexity score."""
|
|
20
18
|
SHALLOW = "shallow"
|
|
21
19
|
STANDARD = "standard"
|
|
22
20
|
DEEP = "deep"
|
|
23
21
|
|
|
24
22
|
|
|
25
|
-
class ForgeStatus(str, Enum):
|
|
23
|
+
class ForgeStatus(str, Enum): # noqa: UP042 — StrEnum flips str()/format(); migration is its own change
|
|
26
24
|
"""Lifecycle status of a ForgePlan."""
|
|
27
25
|
DRAFT = "draft"
|
|
28
26
|
REVIEWING = "reviewing"
|
|
@@ -34,21 +32,21 @@ class ForgeStatus(str, Enum):
|
|
|
34
32
|
ARCHIVED = "archived"
|
|
35
33
|
|
|
36
34
|
|
|
37
|
-
class ExplorerLens(str, Enum):
|
|
35
|
+
class ExplorerLens(str, Enum): # noqa: UP042 — StrEnum flips str()/format(); migration is its own change
|
|
38
36
|
"""The analytical perspective used when exploring a plan."""
|
|
39
37
|
PRAGMATIC = "pragmatic" # Focus on fastest viable path
|
|
40
38
|
ARCHITECTURAL = "architectural" # Focus on long-term design health
|
|
41
39
|
CONTRARIAN = "contrarian" # Challenge assumptions, surface risks
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
class RiskSeverity(str, Enum):
|
|
42
|
+
class RiskSeverity(str, Enum): # noqa: UP042 — StrEnum flips str()/format(); migration is its own change
|
|
45
43
|
"""Severity level for identified risks."""
|
|
46
44
|
LOW = "low"
|
|
47
45
|
MEDIUM = "medium"
|
|
48
46
|
HIGH = "high"
|
|
49
47
|
|
|
50
48
|
|
|
51
|
-
class ExecutionPathType(str, Enum):
|
|
49
|
+
class ExecutionPathType(str, Enum): # noqa: UP042 — StrEnum flips str()/format(); migration is its own change
|
|
52
50
|
"""Type of execution artefact that fulfils a plan step."""
|
|
53
51
|
SKILL = "skill"
|
|
54
52
|
WORKFLOW = "workflow"
|
|
@@ -60,12 +58,22 @@ class ExecutionPathType(str, Enum):
|
|
|
60
58
|
# ---------------------------------------------------------------------------
|
|
61
59
|
|
|
62
60
|
class ComplexityDimensions(BaseModel):
|
|
63
|
-
"""Five-axis complexity breakdown, each scored 0
|
|
61
|
+
"""Five-axis complexity breakdown, each scored 0-100."""
|
|
64
62
|
|
|
65
|
-
scope: int = Field(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
scope: int = Field(
|
|
64
|
+
default=0, description="Breadth of change across the codebase or system."
|
|
65
|
+
)
|
|
66
|
+
dependencies: int = Field(
|
|
67
|
+
default=0,
|
|
68
|
+
description="Number and criticality of upstream/downstream dependencies.",
|
|
69
|
+
)
|
|
70
|
+
ambiguity: int = Field(
|
|
71
|
+
default=0, description="How unclear or under-specified the requirements are."
|
|
72
|
+
)
|
|
73
|
+
risk: int = Field(
|
|
74
|
+
default=0,
|
|
75
|
+
description="Potential for breakage, data loss, or security impact.",
|
|
76
|
+
)
|
|
69
77
|
novelty: int = Field(default=0, description="How unlike existing patterns this work is.")
|
|
70
78
|
|
|
71
79
|
@field_validator("scope", "dependencies", "ambiguity", "risk", "novelty", mode="before")
|
|
@@ -78,14 +86,19 @@ class ComplexityDimensions(BaseModel):
|
|
|
78
86
|
class ComplexityScore(BaseModel):
|
|
79
87
|
"""Aggregated complexity result produced by the Complexity Scorer."""
|
|
80
88
|
|
|
81
|
-
score: int = Field(default=0, description="Composite 0
|
|
82
|
-
tier: ForgeTier = Field(
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
score: int = Field(default=0, description="Composite 0-100 score derived from all dimensions.")
|
|
90
|
+
tier: ForgeTier = Field(
|
|
91
|
+
default=ForgeTier.SHALLOW,
|
|
92
|
+
description="Execution tier selected based on the composite score.",
|
|
93
|
+
)
|
|
94
|
+
dimensions: ComplexityDimensions = Field(
|
|
95
|
+
default_factory=ComplexityDimensions, description="Per-dimension breakdown."
|
|
96
|
+
)
|
|
97
|
+
similar_plans: list[str] = Field(
|
|
85
98
|
default_factory=list,
|
|
86
99
|
description="IDs of previously completed plans with similar profiles.",
|
|
87
100
|
)
|
|
88
|
-
reused_patterns:
|
|
101
|
+
reused_patterns: list[str] = Field(
|
|
89
102
|
default_factory=list,
|
|
90
103
|
description="Named patterns from the ArkaOS pattern library reused in this plan.",
|
|
91
104
|
)
|
|
@@ -213,13 +226,23 @@ class ForgePlan(BaseModel):
|
|
|
213
226
|
"contains only constitution-enforced phases and confidence is 0.0."
|
|
214
227
|
),
|
|
215
228
|
)
|
|
216
|
-
dispatch_errors:
|
|
229
|
+
dispatch_errors: list[str] = Field(
|
|
217
230
|
default_factory=list,
|
|
218
231
|
description="Dispatch failures collected during forge (lens: error class: message).",
|
|
219
232
|
)
|
|
220
233
|
|
|
221
234
|
status: ForgeStatus = ForgeStatus.DRAFT
|
|
222
|
-
approved_at:
|
|
223
|
-
approved_by:
|
|
224
|
-
|
|
225
|
-
|
|
235
|
+
approved_at: str | None = None
|
|
236
|
+
approved_by: str | None = None
|
|
237
|
+
rejected_at: str | None = None
|
|
238
|
+
rejected_by: str | None = None
|
|
239
|
+
executed_at: str | None = None
|
|
240
|
+
completion_notes: str | None = None
|
|
241
|
+
review_note: str | None = Field(
|
|
242
|
+
default=None,
|
|
243
|
+
description=(
|
|
244
|
+
"Operator note attached at decision time (plan-canvas review) "
|
|
245
|
+
"— the 'ponto-e-anota' trail; distinct from completion_notes, "
|
|
246
|
+
"which belongs to execution."
|
|
247
|
+
),
|
|
248
|
+
)
|