arkaos 4.6.0 → 4.7.1
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/CONSTITUTION.md +30 -16
- package/VERSION +1 -1
- package/arka/SKILL.md +26 -36
- package/arka/skills/bootstrap-agent/SKILL.md +6 -0
- package/arka/skills/checkpoint/SKILL.md +1 -1
- package/arka/skills/costs/SKILL.md +14 -5
- package/arka/skills/flow/SKILL.md +30 -15
- package/arka/skills/forge/SKILL.md +19 -15
- package/arka/skills/human-writing/SKILL.md +16 -7
- package/arka/skills/research/SKILL.md +20 -7
- package/arka/templates/skill-template.md +102 -0
- package/bin/arka-py.ps1 +9 -2
- package/config/claude-agents/architect.md +51 -0
- package/config/claude-agents/backend-dev.md +51 -0
- package/config/claude-agents/dba.md +51 -0
- package/config/claude-agents/devops-eng.md +51 -0
- package/config/claude-agents/eduardo-copy.md +14 -7
- package/config/claude-agents/francisca-tech.md +24 -6
- package/config/claude-agents/frontend-dev.md +51 -0
- package/config/claude-agents/marta-cqo.md +28 -8
- package/config/claude-agents/qa-eng.md +51 -0
- package/config/claude-agents/research-assistant.md +50 -0
- package/config/claude-agents/security-eng.md +51 -0
- package/config/constitution.yaml +127 -88
- package/config/hooks/session-start.ps1 +3 -1
- package/config/hooks/session-start.sh +3 -1
- package/config/hooks/user-prompt-submit-v2.sh +4 -11
- package/config/hooks/user-prompt-submit.ps1 +4 -7
- package/config/hooks/user-prompt-submit.sh +8 -1
- package/core/agents/__pycache__/behavioral_compiler.cpython-313.pyc +0 -0
- package/core/agents/behavioral_compiler.py +294 -0
- package/core/budget/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/budget/__pycache__/manager.cpython-314.pyc +0 -0
- package/core/budget/__pycache__/schema.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/complexity.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/handoff.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/persistence.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/renderer.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-314.pyc +0 -0
- package/core/forge/__pycache__/schema.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/agent_experiences.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/phantom_action_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/phantom_action_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/quality_api.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/quality_router.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-314.pyc +0 -0
- package/core/governance/closing_marker_check.py +2 -2
- package/core/governance/phantom_action_check.py +221 -0
- package/core/governance/qg_verdict.py +12 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-314.pyc +0 -0
- package/core/hooks/stop.py +25 -0
- package/core/hooks/user_prompt_submit.py +15 -10
- package/core/knowledge/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/chunker.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/pattern_cards.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-314.pyc +0 -0
- package/core/memory/__pycache__/rehydrator.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/claude_code.py +2 -1
- package/core/runtime/codex_cli.py +2 -0
- package/core/runtime/gemini_cli.py +2 -0
- package/core/shared/__pycache__/temp_paths.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/agent_experiences_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/agent_experiences_layer.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/graph_context_layer.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/pattern_library_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/pattern_library_layer.cpython-314.pyc +0 -0
- package/core/synapse/agent_experiences_layer.py +4 -0
- package/core/synapse/engine.py +13 -2
- package/core/synapse/graph_context_layer.py +4 -0
- package/core/synapse/layers.py +28 -49
- package/core/synapse/pattern_library_layer.py +4 -0
- package/departments/brand/SKILL.md +5 -13
- package/departments/brand/skills/archetype-finder/SKILL.md +3 -11
- package/departments/brand/skills/colors/SKILL.md +3 -11
- package/departments/brand/skills/design-review/SKILL.md +11 -3
- package/departments/brand/skills/design-system/SKILL.md +3 -11
- package/departments/brand/skills/identity-system/SKILL.md +3 -11
- package/departments/brand/skills/logo-brief/SKILL.md +3 -11
- package/departments/brand/skills/mockup-generate/SKILL.md +3 -11
- package/departments/brand/skills/naming-evaluate/SKILL.md +3 -11
- package/departments/brand/skills/positioning-statement/SKILL.md +3 -11
- package/departments/brand/skills/primal-audit/SKILL.md +3 -11
- package/departments/brand/skills/ux-audit/SKILL.md +3 -11
- package/departments/brand/skills/voice-guide/SKILL.md +3 -11
- package/departments/brand/skills/wireframe/SKILL.md +3 -11
- package/departments/community/SKILL.md +4 -12
- package/departments/community/skills/ai-community/SKILL.md +3 -11
- package/departments/community/skills/betting-setup/SKILL.md +3 -11
- package/departments/community/skills/business-model/SKILL.md +3 -11
- package/departments/community/skills/content-calendar/SKILL.md +3 -11
- package/departments/community/skills/events-plan/SKILL.md +3 -11
- package/departments/community/skills/gamification-design/SKILL.md +3 -11
- package/departments/community/skills/growth-plan/SKILL.md +13 -12
- package/departments/community/skills/metrics-track/SKILL.md +3 -11
- package/departments/community/skills/moderation/SKILL.md +3 -11
- package/departments/community/skills/monetize-plan/SKILL.md +3 -11
- package/departments/community/skills/niche-setup/SKILL.md +3 -11
- package/departments/community/skills/onboarding-flow/SKILL.md +3 -11
- package/departments/community/skills/platform-select/SKILL.md +3 -11
- package/departments/content/SKILL.md +4 -12
- package/departments/content/skills/ai-workflow/SKILL.md +3 -11
- package/departments/content/skills/analytics/SKILL.md +3 -11
- package/departments/content/skills/calendar/SKILL.md +3 -11
- package/departments/content/skills/content-system/SKILL.md +3 -11
- package/departments/content/skills/monetization-plan/SKILL.md +3 -11
- package/departments/content/skills/newsletter-write/SKILL.md +3 -11
- package/departments/content/skills/platform-optimize/SKILL.md +3 -11
- package/departments/content/skills/repurpose-plan/SKILL.md +3 -11
- package/departments/content/skills/script-structure/SKILL.md +3 -11
- package/departments/content/skills/short-form/SKILL.md +3 -11
- package/departments/content/skills/thumbnail-package/SKILL.md +3 -11
- package/departments/content/skills/viral-design/SKILL.md +3 -11
- package/departments/content/skills/youtube-strategy/SKILL.md +3 -11
- package/departments/dev/SKILL.md +4 -12
- package/departments/dev/agents/security-eng.yaml +1 -1
- package/departments/dev/skills/adversarial-review/SKILL.md +10 -2
- package/departments/dev/skills/ai-assisted-dev/SKILL.md +3 -11
- package/departments/dev/skills/architecture-design/SKILL.md +3 -11
- package/departments/dev/skills/clean-code-review/SKILL.md +9 -2
- package/departments/dev/skills/code-review/SKILL.md +12 -12
- package/departments/dev/skills/db-design/SKILL.md +3 -11
- package/departments/dev/skills/ddd-model/SKILL.md +3 -11
- package/departments/dev/skills/demo-gif/SKILL.md +3 -11
- package/departments/dev/skills/deploy/SKILL.md +3 -11
- package/departments/dev/skills/devops-pipeline/SKILL.md +3 -11
- package/departments/dev/skills/docs/SKILL.md +3 -11
- package/departments/dev/skills/mcp/SKILL.md +3 -11
- package/departments/dev/skills/performance-audit/SKILL.md +3 -11
- package/departments/dev/skills/refactor-plan/SKILL.md +3 -11
- package/departments/dev/skills/research/SKILL.md +16 -12
- package/departments/dev/skills/scaffold/SKILL.md +15 -11
- package/departments/dev/skills/security-compliance/SKILL.md +3 -11
- package/departments/dev/skills/spec/SKILL.md +15 -10
- package/departments/dev/skills/stack-check/SKILL.md +3 -11
- package/departments/ecom/SKILL.md +4 -12
- package/departments/ecom/skills/analytics/SKILL.md +3 -11
- package/departments/ecom/skills/browse-competitor/SKILL.md +3 -11
- package/departments/ecom/skills/cart-recovery/SKILL.md +3 -11
- package/departments/ecom/skills/cro-optimize/SKILL.md +3 -11
- package/departments/ecom/skills/customer-journey/SKILL.md +3 -11
- package/departments/ecom/skills/fulfillment-plan/SKILL.md +3 -11
- package/departments/ecom/skills/marketplace-manage/SKILL.md +3 -11
- package/departments/ecom/skills/pricing-strategy/SKILL.md +3 -11
- package/departments/ecom/skills/product-launch/SKILL.md +3 -11
- package/departments/ecom/skills/rfm-segment/SKILL.md +3 -11
- package/departments/ecom/skills/social-commerce/SKILL.md +3 -11
- package/departments/ecom/skills/store-audit/SKILL.md +3 -11
- package/departments/ecom/skills/subscription-model/SKILL.md +3 -11
- package/departments/finance/SKILL.md +4 -12
- package/departments/finance/skills/budget-plan/SKILL.md +3 -11
- package/departments/finance/skills/cashflow-forecast/SKILL.md +3 -11
- package/departments/finance/skills/ciso-advisor/SKILL.md +3 -11
- package/departments/finance/skills/financial-model/SKILL.md +3 -11
- package/departments/finance/skills/pitch-deck/SKILL.md +3 -11
- package/departments/finance/skills/scenario-analysis/SKILL.md +3 -11
- package/departments/finance/skills/unit-economics/SKILL.md +3 -11
- package/departments/finance/skills/valuation-model/SKILL.md +3 -11
- package/departments/kb/SKILL.md +4 -12
- package/departments/kb/skills/ai-research/SKILL.md +3 -11
- package/departments/kb/skills/competitive-intel/SKILL.md +3 -11
- package/departments/kb/skills/knowledge-review/SKILL.md +3 -11
- package/departments/kb/skills/learn-content/SKILL.md +3 -11
- package/departments/kb/skills/moc-create/SKILL.md +3 -11
- package/departments/kb/skills/persona-build/SKILL.md +3 -11
- package/departments/kb/skills/research-plan/SKILL.md +3 -11
- package/departments/kb/skills/search-kb/SKILL.md +3 -11
- package/departments/kb/skills/source-evaluate/SKILL.md +3 -11
- package/departments/kb/skills/taxonomy-manage/SKILL.md +3 -11
- package/departments/kb/skills/write-as-persona/SKILL.md +3 -11
- package/departments/landing/SKILL.md +4 -12
- package/departments/landing/skills/ab-test/SKILL.md +3 -11
- package/departments/landing/skills/affiliate-bridge/SKILL.md +3 -11
- package/departments/landing/skills/awareness-diagnose/SKILL.md +3 -11
- package/departments/landing/skills/copy-framework/SKILL.md +1 -1
- package/departments/landing/skills/email-sequence/SKILL.md +3 -11
- package/departments/landing/skills/funnel-metrics/SKILL.md +3 -11
- package/departments/landing/skills/headline-write/SKILL.md +3 -11
- package/departments/landing/skills/launch-sequence/SKILL.md +3 -11
- package/departments/landing/skills/offer-create/SKILL.md +3 -11
- package/departments/landing/skills/optimize-page/SKILL.md +3 -11
- package/departments/landing/skills/page-architect/SKILL.md +3 -11
- package/departments/landing/skills/persuasion-apply/SKILL.md +3 -11
- package/departments/landing/skills/webinar-funnel/SKILL.md +3 -11
- package/departments/leadership/SKILL.md +4 -12
- package/departments/leadership/skills/change-manage/SKILL.md +3 -11
- package/departments/leadership/skills/conflict-resolve/SKILL.md +3 -11
- package/departments/leadership/skills/culture-audit/SKILL.md +3 -11
- package/departments/leadership/skills/delegation-matrix/SKILL.md +3 -11
- package/departments/leadership/skills/disc-assess/SKILL.md +3 -11
- package/departments/leadership/skills/feedback-give/SKILL.md +3 -11
- package/departments/leadership/skills/hiring-plan/SKILL.md +3 -11
- package/departments/leadership/skills/performance-review/SKILL.md +3 -11
- package/departments/marketing/SKILL.md +4 -12
- package/departments/marketing/skills/ab-test/SKILL.md +3 -11
- package/departments/marketing/skills/analytics-report/SKILL.md +3 -11
- package/departments/marketing/skills/audience-segment/SKILL.md +3 -11
- package/departments/marketing/skills/calendar-plan/SKILL.md +3 -11
- package/departments/marketing/skills/competitor-analysis/SKILL.md +3 -11
- package/departments/marketing/skills/content-audit/SKILL.md +3 -11
- package/departments/marketing/skills/email-sequence/SKILL.md +3 -11
- package/departments/marketing/skills/growth-loop/SKILL.md +15 -13
- package/departments/marketing/skills/marketing-automation/SKILL.md +3 -11
- package/departments/marketing/skills/paid-campaign/SKILL.md +3 -11
- package/departments/marketing/skills/programmatic-seo/SKILL.md +3 -11
- package/departments/marketing/skills/seo-audit/SKILL.md +3 -11
- package/departments/marketing/skills/social-strategy/SKILL.md +3 -11
- package/departments/ops/SKILL.md +4 -12
- package/departments/ops/skills/bottleneck-find/SKILL.md +3 -11
- package/departments/ops/skills/dashboard-build/SKILL.md +3 -11
- package/departments/ops/skills/gdpr-compliance/SKILL.md +3 -11
- package/departments/ops/skills/gtd-setup/SKILL.md +3 -11
- package/departments/ops/skills/integration-design/SKILL.md +3 -11
- package/departments/ops/skills/iso27001/SKILL.md +3 -11
- package/departments/ops/skills/lean-audit/SKILL.md +3 -11
- package/departments/ops/skills/metrics-dashboard/SKILL.md +3 -11
- package/departments/ops/skills/n8n-flow/SKILL.md +3 -11
- package/departments/ops/skills/quality-management/SKILL.md +3 -11
- package/departments/ops/skills/risk-management/SKILL.md +3 -11
- package/departments/ops/skills/soc2-compliance/SKILL.md +3 -11
- package/departments/ops/skills/sop-create/SKILL.md +3 -11
- package/departments/ops/skills/workflow-automate/SKILL.md +3 -11
- package/departments/ops/skills/zapier-flow/SKILL.md +3 -11
- package/departments/org/SKILL.md +4 -12
- package/departments/org/skills/compensation-plan/SKILL.md +3 -11
- package/departments/org/skills/culture-define/SKILL.md +3 -11
- package/departments/org/skills/decision-framework/SKILL.md +3 -11
- package/departments/org/skills/hiring-plan/SKILL.md +3 -11
- package/departments/org/skills/meeting-optimize/SKILL.md +3 -11
- package/departments/org/skills/onboarding-design/SKILL.md +3 -11
- package/departments/org/skills/org-design/SKILL.md +3 -11
- package/departments/org/skills/remote-setup/SKILL.md +3 -11
- package/departments/org/skills/sop-process/SKILL.md +3 -11
- package/departments/org/skills/team-assess/SKILL.md +3 -11
- package/departments/pm/SKILL.md +4 -12
- package/departments/pm/skills/backlog-groom/SKILL.md +3 -11
- package/departments/pm/skills/discovery-plan/SKILL.md +3 -11
- package/departments/pm/skills/estimate-forecast/SKILL.md +3 -11
- package/departments/pm/skills/impact-map/SKILL.md +3 -11
- package/departments/pm/skills/kanban-setup/SKILL.md +3 -11
- package/departments/pm/skills/risk-register/SKILL.md +3 -11
- package/departments/pm/skills/roadmap-build/SKILL.md +3 -11
- package/departments/pm/skills/sprint-plan/SKILL.md +3 -11
- package/departments/pm/skills/stakeholder-map/SKILL.md +3 -11
- package/departments/pm/skills/standup-run/SKILL.md +3 -11
- package/departments/pm/skills/story-write/SKILL.md +3 -11
- package/departments/quality/agents/copy-director.yaml +10 -4
- package/departments/saas/SKILL.md +4 -12
- package/departments/saas/skills/benchmark-compare/SKILL.md +3 -11
- package/departments/saas/skills/churn-analysis/SKILL.md +3 -11
- package/departments/saas/skills/customer-success/SKILL.md +3 -11
- package/departments/saas/skills/growth-plan/SKILL.md +13 -12
- package/departments/saas/skills/gtm-strategy/SKILL.md +3 -11
- package/departments/saas/skills/launch-execute/SKILL.md +3 -11
- package/departments/saas/skills/metrics-dashboard/SKILL.md +3 -11
- package/departments/saas/skills/micro-saas-stack/SKILL.md +3 -11
- package/departments/saas/skills/mvp-build/SKILL.md +3 -11
- package/departments/saas/skills/niche-evaluate/SKILL.md +13 -12
- package/departments/saas/skills/onboarding-optimize/SKILL.md +3 -11
- package/departments/saas/skills/plg-setup/SKILL.md +3 -11
- package/departments/saas/skills/pricing-strategy/SKILL.md +3 -11
- package/departments/saas/skills/validate-idea/SKILL.md +13 -13
- package/departments/sales/SKILL.md +4 -12
- package/departments/sales/skills/challenger-sell/SKILL.md +3 -11
- package/departments/sales/skills/deal-qualify/SKILL.md +3 -11
- package/departments/sales/skills/discovery-call/SKILL.md +3 -11
- package/departments/sales/skills/forecast-revenue/SKILL.md +3 -11
- package/departments/sales/skills/negotiate-plan/SKILL.md +3 -11
- package/departments/sales/skills/objection-handle/SKILL.md +3 -11
- package/departments/sales/skills/pipeline-manage/SKILL.md +3 -11
- package/departments/sales/skills/pricing-negotiate/SKILL.md +3 -11
- package/departments/strategy/SKILL.md +4 -12
- package/departments/strategy/skills/blue-ocean/SKILL.md +3 -11
- package/departments/strategy/skills/bmc/SKILL.md +3 -11
- package/departments/strategy/skills/board-advisor/SKILL.md +3 -11
- package/departments/strategy/skills/cto-advisor/SKILL.md +3 -11
- package/departments/strategy/skills/extract-data/SKILL.md +3 -11
- package/departments/strategy/skills/five-forces/SKILL.md +3 -11
- package/departments/strategy/skills/growth-strategy/SKILL.md +15 -14
- package/departments/strategy/skills/moat-analysis/SKILL.md +3 -11
- package/departments/strategy/skills/position/SKILL.md +3 -11
- package/departments/strategy/skills/scenario-plan/SKILL.md +3 -11
- package/knowledge/commands-registry.json +3006 -1676
- package/knowledge/commands-registry.json.bak +4121 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/migrate_skills_kb_first.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-314.pyc +0 -0
- package/scripts/migrate_skills_kb_first.py +41 -21
- package/scripts/tools/__pycache__/prompt_lint.cpython-313.pyc +0 -0
- package/scripts/tools/prompt_lint.py +276 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Behavioral compiler: agent YAML → runtime subagent markdown (PR-4).
|
|
2
|
+
|
|
3
|
+
The 2026-07-08 frontier prompt audit found that psychometric tables
|
|
4
|
+
(DISC scores, Big Five 0-100, MBTI) never reach the model and cannot be
|
|
5
|
+
operationalized by it — zero production prompts from any frontier lab
|
|
6
|
+
use trait taxonomies; all use behavioral specification. This compiler
|
|
7
|
+
keeps the 4-framework DNA as the DESIGN-TIME source (it differentiates
|
|
8
|
+
82 agents) and emits the RUNTIME translation: conditional rules, a
|
|
9
|
+
lexical blacklist, a disagreement/escalation protocol with an exit
|
|
10
|
+
condition, and the persona-vs-artifact contract.
|
|
11
|
+
|
|
12
|
+
Single source: `departments/<dept>/agents/<slug>.yaml`. Output:
|
|
13
|
+
`config/claude-agents/<slug>.md` (what the installer copies into every
|
|
14
|
+
project's `.claude/agents/`). Hand-written prompts in _HAND_WRITTEN are
|
|
15
|
+
never overwritten. `tests/python/test_behavioral_compiler.py` locks
|
|
16
|
+
committed output byte-identical to `compile_agent()` of its YAML.
|
|
17
|
+
|
|
18
|
+
CLI:
|
|
19
|
+
arka-py -m core.agents.behavioral_compiler --check # drift check
|
|
20
|
+
arka-py -m core.agents.behavioral_compiler --write # (re)generate
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
import yaml
|
|
29
|
+
|
|
30
|
+
_ROOT = Path(__file__).resolve().parents[2]
|
|
31
|
+
_OUT_DIR = _ROOT / "config" / "claude-agents"
|
|
32
|
+
|
|
33
|
+
# Best hand-authored prompts in the repo (QG + dev lead) — the compiler
|
|
34
|
+
# must never clobber them.
|
|
35
|
+
_HAND_WRITTEN = {"marta-cqo", "eduardo-copy", "francisca-tech", "paulo-tech-lead"}
|
|
36
|
+
|
|
37
|
+
# Pilot scope: dev department. tech-lead maps to the hand-written
|
|
38
|
+
# paulo-tech-lead.md and is excluded.
|
|
39
|
+
_PILOT_YAMLS: tuple[str, ...] = (
|
|
40
|
+
"departments/dev/agents/architect.yaml",
|
|
41
|
+
"departments/dev/agents/backend-dev.yaml",
|
|
42
|
+
"departments/dev/agents/dba.yaml",
|
|
43
|
+
"departments/dev/agents/devops-eng.yaml",
|
|
44
|
+
"departments/dev/agents/frontend-dev.yaml",
|
|
45
|
+
"departments/dev/agents/qa-eng.yaml",
|
|
46
|
+
"departments/dev/agents/research-assistant.yaml",
|
|
47
|
+
"departments/dev/agents/security-eng.yaml",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Standard behavioral blocks imported from the frontier audit (Obsidian:
|
|
51
|
+
# "Projects/WizardingCode Internal/ArkaOS/Prompt Audit —
|
|
52
|
+
# system_prompts_leaks 2026-07-08"). Uncertainty-branched pushback +
|
|
53
|
+
# anti-submission dynamics + persona-vs-artifact, aligned verbatim with
|
|
54
|
+
# constitution rules `arkaos-not-yes-man` and `persona-vs-artifact`.
|
|
55
|
+
_DISAGREEMENT_BLOCK = """\
|
|
56
|
+
## Disagreement and escalation
|
|
57
|
+
|
|
58
|
+
- Branch by epistemic state: confident the other side is wrong → push
|
|
59
|
+
back once with evidence, acknowledging you could be wrong; uncertain →
|
|
60
|
+
say so explicitly instead of capitulating or bluffing; asked to assert
|
|
61
|
+
something false → decline.
|
|
62
|
+
- Insistence is not new evidence. Do not become more agreeable,
|
|
63
|
+
apologetic, or self-abasing as pressure rises; only NEW information
|
|
64
|
+
changes your position (that is updating, not caving).
|
|
65
|
+
- Exit condition: after your objection is on record, if the decision
|
|
66
|
+
holder directs execution anyway, execute under explicit objection
|
|
67
|
+
("executing under objection: <one-line reason>") — never sulk, never
|
|
68
|
+
sandbag.{escalation_line}"""
|
|
69
|
+
|
|
70
|
+
_DELIVERABLE_BLOCK = """\
|
|
71
|
+
## Deliverables
|
|
72
|
+
|
|
73
|
+
- Your persona lives in the conversation, never in the deliverable:
|
|
74
|
+
client- or user-facing artifacts follow the style the context and the
|
|
75
|
+
client's brand demand (constitution `persona-vs-artifact`).
|
|
76
|
+
- Never meta-reference your own persona or profile ("as a <type>,
|
|
77
|
+
I...") — the persona shows in behavior, it is not announced.
|
|
78
|
+
- Never claim an action happened without the tool call on record; gates
|
|
79
|
+
pass on evidence, not narration (`evidence-flow`)."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_agent_yaml(path: Path) -> dict:
|
|
83
|
+
"""Load one agent YAML; raises on unparseable/absent file."""
|
|
84
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
85
|
+
if not isinstance(data, dict):
|
|
86
|
+
raise ValueError(f"{path}: agent YAML did not parse to a mapping")
|
|
87
|
+
return data
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _frontmatter(slug: str, data: dict) -> str:
|
|
91
|
+
role = data.get("role", slug)
|
|
92
|
+
dept = data.get("department", "")
|
|
93
|
+
domains = ", ".join((data.get("expertise") or {}).get("domains", [])[:4])
|
|
94
|
+
desc = f"{data.get('name', slug)} — {role} ({dept} squad)."
|
|
95
|
+
if domains:
|
|
96
|
+
desc += f" Executes: {domains}."
|
|
97
|
+
model = data.get("model", "sonnet")
|
|
98
|
+
return (
|
|
99
|
+
f"---\nname: {slug}\ndescription: >\n {desc}\nmodel: {model}\n---"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _identity(data: dict) -> str:
|
|
104
|
+
dna = data.get("behavioral_dna") or {}
|
|
105
|
+
enn = dna.get("enneagram") or {}
|
|
106
|
+
name, role = data.get("name", "?"), data.get("role", "?")
|
|
107
|
+
dept, tier = data.get("department", "?"), data.get("tier", "?")
|
|
108
|
+
lines = [f"You are {name}, {role} of the {dept} squad (Tier {tier})."]
|
|
109
|
+
if enn.get("core_motivation"):
|
|
110
|
+
lines.append(f"What drives you: {enn['core_motivation']}.")
|
|
111
|
+
if enn.get("core_fear"):
|
|
112
|
+
lines.append(f"The failure you exist to prevent: {enn['core_fear']}.")
|
|
113
|
+
return " ".join(lines)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _work_rules(data: dict) -> list[str]:
|
|
117
|
+
# Label form throughout (QG B1/B2): DISC/communication fields are
|
|
118
|
+
# 3rd-person descriptors, not verb complements — gluing them to an
|
|
119
|
+
# imperative produced ungrammatical prompts in 8/8 pilot files.
|
|
120
|
+
dna = data.get("behavioral_dna") or {}
|
|
121
|
+
disc = dna.get("disc") or {}
|
|
122
|
+
comm = data.get("communication") or {}
|
|
123
|
+
rules: list[str] = []
|
|
124
|
+
if disc.get("communication_style"):
|
|
125
|
+
rules.append(f"Communication: {disc['communication_style']}.")
|
|
126
|
+
if comm.get("tone"):
|
|
127
|
+
rules.append(f"Tone: {comm['tone']}.")
|
|
128
|
+
if comm.get("preferred_format"):
|
|
129
|
+
rules.append(f"Default output shape: {comm['preferred_format']}.")
|
|
130
|
+
if disc.get("under_pressure"):
|
|
131
|
+
rules.append(f"Under pressure: {_lc(disc['under_pressure'])}.")
|
|
132
|
+
for item in comm.get("avoid", []):
|
|
133
|
+
rules.append(f"Avoid {item}.")
|
|
134
|
+
return rules
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _lc(text: str) -> str:
|
|
138
|
+
return text[0].lower() + text[1:] if text else text
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def build_escalation_index(base: Path) -> dict[str, str]:
|
|
142
|
+
"""Map every agent YAML id to a rendered escalation target.
|
|
143
|
+
|
|
144
|
+
Deployed subagents (pilot slugs + hand-written) render as a backtick
|
|
145
|
+
handle; everyone else renders by role and name — a handle that
|
|
146
|
+
resolves to no deployed subagent is worse than no handle (QG B3).
|
|
147
|
+
"""
|
|
148
|
+
deployed = {out.stem for _, out in pilot_targets(base)} | _HAND_WRITTEN
|
|
149
|
+
slug_alias = {"tech-lead": "paulo-tech-lead"}
|
|
150
|
+
index: dict[str, str] = {}
|
|
151
|
+
for yaml_path in sorted(base.glob("departments/*/agents/*.yaml")):
|
|
152
|
+
try:
|
|
153
|
+
data = yaml.safe_load(yaml_path.read_text(encoding="utf-8"))
|
|
154
|
+
except Exception:
|
|
155
|
+
continue
|
|
156
|
+
if not isinstance(data, dict) or not data.get("id"):
|
|
157
|
+
continue
|
|
158
|
+
slug = slug_alias.get(yaml_path.stem, yaml_path.stem)
|
|
159
|
+
if slug in deployed:
|
|
160
|
+
index[data["id"]] = f"`{slug}`"
|
|
161
|
+
else:
|
|
162
|
+
role = data.get("role", yaml_path.stem)
|
|
163
|
+
name = data.get("name", "")
|
|
164
|
+
index[data["id"]] = f"the {role}" + (f" ({name})" if name else "")
|
|
165
|
+
return index
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _blacklist(data: dict) -> list[str]:
|
|
169
|
+
markers = data.get("signature_markers") or {}
|
|
170
|
+
seen: list[str] = []
|
|
171
|
+
for phrase in markers.get("avoid_patterns", []):
|
|
172
|
+
if phrase not in seen:
|
|
173
|
+
seen.append(phrase)
|
|
174
|
+
return seen
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _grounding(data: dict) -> str:
|
|
178
|
+
exp = data.get("expertise") or {}
|
|
179
|
+
models = data.get("mental_models") or {}
|
|
180
|
+
frameworks = list(exp.get("frameworks", [])) or list(
|
|
181
|
+
models.get("primary", [])
|
|
182
|
+
)
|
|
183
|
+
if not frameworks:
|
|
184
|
+
return ""
|
|
185
|
+
joined = ", ".join(frameworks)
|
|
186
|
+
return (
|
|
187
|
+
"## Grounding\n\nRecommendations cite the framework behind them — "
|
|
188
|
+
f"yours: {joined}. A recommendation without a named framework or "
|
|
189
|
+
"reference is an opinion; label it as such."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def compile_agent(
|
|
194
|
+
data: dict,
|
|
195
|
+
slug: str,
|
|
196
|
+
yaml_rel_path: str,
|
|
197
|
+
escalation_index: dict[str, str] | None = None,
|
|
198
|
+
) -> str:
|
|
199
|
+
"""Compile one agent YAML dict into runtime subagent markdown."""
|
|
200
|
+
esc = (data.get("authority") or {}).get("escalates_to")
|
|
201
|
+
target = (escalation_index or {}).get(esc, esc) if esc else None
|
|
202
|
+
escalation_line = (
|
|
203
|
+
f"\n- Scope exceeded or deadlock after objection: escalate to "
|
|
204
|
+
f"{target}." if target else ""
|
|
205
|
+
)
|
|
206
|
+
sections = [
|
|
207
|
+
_frontmatter(slug, data),
|
|
208
|
+
f"<!-- generated by core/agents/behavioral_compiler.py from "
|
|
209
|
+
f"{yaml_rel_path} — DO NOT EDIT; edit the YAML and re-run -->",
|
|
210
|
+
f"# {data.get('name', slug)} — {data.get('role', slug)}",
|
|
211
|
+
_identity(data),
|
|
212
|
+
]
|
|
213
|
+
work_rules = _work_rules(data)
|
|
214
|
+
if work_rules:
|
|
215
|
+
sections.append(
|
|
216
|
+
"## How you work\n\n" + "\n".join(f"- {r}" for r in work_rules)
|
|
217
|
+
)
|
|
218
|
+
blacklist = _blacklist(data)
|
|
219
|
+
if blacklist:
|
|
220
|
+
sections.append(
|
|
221
|
+
"## Never write\n\nThese exact phrases (any casing) are "
|
|
222
|
+
"banned from your output:\n"
|
|
223
|
+
+ "\n".join(f'- "{p}"' for p in blacklist)
|
|
224
|
+
)
|
|
225
|
+
sections.append(_DISAGREEMENT_BLOCK.format(escalation_line=escalation_line))
|
|
226
|
+
sections.append(_DELIVERABLE_BLOCK)
|
|
227
|
+
grounding = _grounding(data)
|
|
228
|
+
if grounding:
|
|
229
|
+
sections.append(grounding)
|
|
230
|
+
return "\n\n".join(s for s in sections if s) + "\n"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def pilot_targets(root: Path | None = None) -> list[tuple[Path, Path]]:
|
|
234
|
+
"""(yaml_path, output_path) pairs for the pilot scope."""
|
|
235
|
+
base = root or _ROOT
|
|
236
|
+
pairs: list[tuple[Path, Path]] = []
|
|
237
|
+
for rel in _PILOT_YAMLS:
|
|
238
|
+
yaml_path = base / rel
|
|
239
|
+
slug = yaml_path.stem
|
|
240
|
+
if slug in _HAND_WRITTEN:
|
|
241
|
+
continue
|
|
242
|
+
pairs.append((yaml_path, base / "config" / "claude-agents" / f"{slug}.md"))
|
|
243
|
+
return pairs
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def check(root: Path | None = None) -> list[str]:
|
|
247
|
+
"""Return drift messages (committed output != compiled YAML)."""
|
|
248
|
+
base = root or _ROOT
|
|
249
|
+
index = build_escalation_index(base)
|
|
250
|
+
problems: list[str] = []
|
|
251
|
+
for yaml_path, out_path in pilot_targets(base):
|
|
252
|
+
expected = compile_agent(
|
|
253
|
+
load_agent_yaml(yaml_path), out_path.stem,
|
|
254
|
+
str(yaml_path.relative_to(base)), index,
|
|
255
|
+
)
|
|
256
|
+
if not out_path.exists():
|
|
257
|
+
problems.append(f"{out_path}: missing — run --write")
|
|
258
|
+
elif out_path.read_text(encoding="utf-8") != expected:
|
|
259
|
+
problems.append(
|
|
260
|
+
f"{out_path}: drifted from {yaml_path.name} — run --write"
|
|
261
|
+
)
|
|
262
|
+
return problems
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def write(root: Path | None = None) -> list[Path]:
|
|
266
|
+
"""(Re)generate every pilot agent; returns written paths."""
|
|
267
|
+
base = root or _ROOT
|
|
268
|
+
index = build_escalation_index(base)
|
|
269
|
+
written: list[Path] = []
|
|
270
|
+
for yaml_path, out_path in pilot_targets(base):
|
|
271
|
+
content = compile_agent(
|
|
272
|
+
load_agent_yaml(yaml_path), out_path.stem,
|
|
273
|
+
str(yaml_path.relative_to(base)), index,
|
|
274
|
+
)
|
|
275
|
+
out_path.write_text(content, encoding="utf-8")
|
|
276
|
+
written.append(out_path)
|
|
277
|
+
return written
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def main(argv: list[str] | None = None) -> int:
|
|
281
|
+
args = argv if argv is not None else sys.argv[1:]
|
|
282
|
+
if "--write" in args:
|
|
283
|
+
for path in write():
|
|
284
|
+
print(f"wrote {path}")
|
|
285
|
+
return 0
|
|
286
|
+
problems = check()
|
|
287
|
+
for problem in problems:
|
|
288
|
+
print(problem)
|
|
289
|
+
print("behavioral-compiler: " + ("drift" if problems else "clean"))
|
|
290
|
+
return 1 if problems else 0
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
sys.exit(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -31,8 +31,8 @@ _TRIVIAL_WORD_THRESHOLD: int = 15
|
|
|
31
31
|
_SUGGESTION_TEXT: str = (
|
|
32
32
|
"Closing marker missing — end every flow-required turn with "
|
|
33
33
|
"`[arka:gate:4] <label>` (evidence flow review gate) or "
|
|
34
|
-
"`[arka:trivial] <reason>` (single-file edit
|
|
35
|
-
"
|
|
34
|
+
"`[arka:trivial] <reason>` (single-file edit under 10 lines). "
|
|
35
|
+
"Without the marker, telemetry can't confirm "
|
|
36
36
|
"the turn closed cleanly."
|
|
37
37
|
)
|
|
38
38
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Phantom-action soft-block (prompt-surface P0 2026-07-08).
|
|
2
|
+
|
|
3
|
+
Response-side classifier. A "phantom action" is prose in the closing
|
|
4
|
+
assistant message claiming a completed EFFECT ("criei o ficheiro X",
|
|
5
|
+
"fiz commit", "I pushed the fix") in a turn that contains ZERO tool_use
|
|
6
|
+
blocks — narrated work without a tool call on record is the grammatical
|
|
7
|
+
form of delivery-theatre the ``evidence-flow`` rule bans. Pattern
|
|
8
|
+
sourced from the 2026-07-08 prompt audit (Obsidian: "Projects/
|
|
9
|
+
WizardingCode Internal/ArkaOS/Prompt Audit — system_prompts_leaks
|
|
10
|
+
2026-07-08", finding "anti-phantom-action"; corpus clone at
|
|
11
|
+
~/AIProjects/system_prompts_leaks).
|
|
12
|
+
|
|
13
|
+
Precision-first: only claims whose completion implies a tool effect are
|
|
14
|
+
flagged. Ambiguous verbs ("escrevi", "wrote", "corri", "ran") count only
|
|
15
|
+
when bound to a concrete effect object (file, commit, tests, module…) —
|
|
16
|
+
"escrevi um resumo abaixo" is response prose, not an effect claim.
|
|
17
|
+
|
|
18
|
+
Mirrors the contract of ``core.governance.closing_marker_check``:
|
|
19
|
+
soft-block, never raises on any input, hooks consume the result and log
|
|
20
|
+
warn-only telemetry before any promotion to hard enforcement.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import re
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
|
|
29
|
+
# Objects whose creation/modification implies a tool effect.
|
|
30
|
+
_EFFECT_OBJECT = (
|
|
31
|
+
r"(?:\b(?:ficheiros?|files?|m[óo]dulos?|modules?|branch(?:es)?|commits?"
|
|
32
|
+
r"|PRs?|pull.requests?|scripts?|testes?|tests?|suites?|classes?"
|
|
33
|
+
r"|fun[çc][õo]es?|functions?|hooks?|configs?|pipelines?|migra[çc][õo]es?"
|
|
34
|
+
r"|migrations?|packages?|releases?|tags?|documentos?|documents?"
|
|
35
|
+
r"|p[áa]ginas?|pages?|componentes?|components?|endpoints?|comandos?"
|
|
36
|
+
r"|commands?|builds?)\b|\.[a-z]{2,4}\b|`[^`]+`)"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Analytic/mental nouns: a verb whose nearest object is one of these is
|
|
40
|
+
# narrating the RESPONSE, not a tool effect ("updated my understanding of
|
|
41
|
+
# the file", "adicionei contexto sobre os testes") — the effect object
|
|
42
|
+
# match must not cross them (QG condition on #255 before enforcement).
|
|
43
|
+
_ANALYTIC_NOUN = (
|
|
44
|
+
r"\b(?:understanding|context|summary|notes?|thinking|thoughts?"
|
|
45
|
+
r"|takeaways?|impressions?|sense|grasp|view|idea|picture"
|
|
46
|
+
r"|mental\s+model|contexto|resumo|notas?|entendimento"
|
|
47
|
+
r"|perce[çc][ãa]o|an[áa]lise|analysis|ideia|vis[ãa]o|imagem"
|
|
48
|
+
r"|no[çc][ãa]o|perspe[ct]?tiva|opini[ãa]o|conhecimento|knowledge)\b"
|
|
49
|
+
)
|
|
50
|
+
_GAP = r"(?:(?!" + _ANALYTIC_NOUN + r")[^.\n!?]){0,60}?"
|
|
51
|
+
|
|
52
|
+
# Verbs whose completion is unambiguous on its own (git/publish/install).
|
|
53
|
+
_STANDALONE_PT = (
|
|
54
|
+
r"(?<!não )\b(instalei|publiquei|gravei|renomeei|implementei"
|
|
55
|
+
r"|fiz\s+(?:o\s+|um\s+)?(?:commit|push|merge|deploy|release|rebase"
|
|
56
|
+
r"|revert))\b"
|
|
57
|
+
)
|
|
58
|
+
_STANDALONE_EN = (
|
|
59
|
+
r"\bI(?:'ve| have)?\s+(?:just\s+)?(committed|pushed|merged|published"
|
|
60
|
+
r"|released|deployed|installed)\b"
|
|
61
|
+
)
|
|
62
|
+
# Ambiguous verbs — only a claim when bound to an effect object nearby,
|
|
63
|
+
# without an analytic noun in between. "ran into"/"ran through" are
|
|
64
|
+
# idioms, never executions.
|
|
65
|
+
_BOUND_PT = (
|
|
66
|
+
r"(?<!não )\b(criei|escrevi|atualizei|editei|apaguei|removi|adicionei"
|
|
67
|
+
r"|movi|apliquei|corri|executei)\b" + _GAP + _EFFECT_OBJECT
|
|
68
|
+
)
|
|
69
|
+
_BOUND_EN = (
|
|
70
|
+
r"\bI(?:'ve| have)?\s+(?:just\s+)?(created|wrote|updated|deleted"
|
|
71
|
+
r"|added|moved|applied|renamed|ran(?!\s+(?:into|through|over|across))"
|
|
72
|
+
r"|executed)\b" + _GAP + _EFFECT_OBJECT
|
|
73
|
+
)
|
|
74
|
+
_PASSIVE = (
|
|
75
|
+
r"\b(commit|push|merge|deploy|release|PR)\s+(feito|criado|efetuado"
|
|
76
|
+
r"|realizado|concluído)\b"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
_CLAIM_PATTERNS: tuple[re.Pattern[str], ...] = tuple(
|
|
80
|
+
re.compile(p, re.IGNORECASE)
|
|
81
|
+
for p in (_STANDALONE_PT, _STANDALONE_EN, _BOUND_PT, _BOUND_EN, _PASSIVE)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
_SUGGESTION_TEXT: str = (
|
|
85
|
+
"Phantom action — the closing message narrates a completed effect but "
|
|
86
|
+
"the turn contains no tool call. Prose in the past tense describing an "
|
|
87
|
+
"action is only valid with the corresponding tool call on record "
|
|
88
|
+
"(evidence-flow: gates pass on evidence, never on narration)."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class PhantomActionResult:
|
|
94
|
+
"""Verdict of a phantom-action check. Immutable; safe to log as JSON."""
|
|
95
|
+
|
|
96
|
+
passed: bool
|
|
97
|
+
reason: str
|
|
98
|
+
claims: list[str] = field(default_factory=list)
|
|
99
|
+
suggestion: str | None = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def find_action_claims(response_text: str) -> list[str]:
|
|
103
|
+
"""Return the distinct completed-effect claims found in the text."""
|
|
104
|
+
text = response_text or ""
|
|
105
|
+
hits: list[str] = []
|
|
106
|
+
for pattern in _CLAIM_PATTERNS:
|
|
107
|
+
for match in pattern.finditer(text):
|
|
108
|
+
token = match.group(0).strip().lower()
|
|
109
|
+
if token not in hits:
|
|
110
|
+
hits.append(token)
|
|
111
|
+
return hits
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def count_turn_tool_uses(raw_transcript: str | None) -> int | None:
|
|
115
|
+
"""Count tool_use blocks in the final assistant turn of a transcript.
|
|
116
|
+
|
|
117
|
+
The turn is every record after the last REAL user message (a user
|
|
118
|
+
record whose content is not exclusively tool_result blocks). Returns
|
|
119
|
+
None when no parseable records exist OR when no real user message is
|
|
120
|
+
found — callers must fail open on None.
|
|
121
|
+
"""
|
|
122
|
+
if not raw_transcript:
|
|
123
|
+
return None
|
|
124
|
+
records = _parse_jsonl(raw_transcript)
|
|
125
|
+
if not records:
|
|
126
|
+
return None
|
|
127
|
+
start = _last_real_user_index(records)
|
|
128
|
+
if start < 0:
|
|
129
|
+
return None
|
|
130
|
+
count = 0
|
|
131
|
+
for record in records[start + 1 :]:
|
|
132
|
+
if _record_role(record) != "assistant":
|
|
133
|
+
continue
|
|
134
|
+
count += _count_tool_use_blocks(record)
|
|
135
|
+
return count
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def check_phantom_actions(
|
|
139
|
+
response_text: str, raw_transcript: str | None
|
|
140
|
+
) -> PhantomActionResult:
|
|
141
|
+
"""Classify whether the closing message narrates unbacked effects.
|
|
142
|
+
|
|
143
|
+
Fail-open at every layer: an unparseable transcript skips the check,
|
|
144
|
+
and any unexpected error returns a passing "check-error" result — a
|
|
145
|
+
false PASS is telemetry noise, a false FAIL (or a crash) erodes
|
|
146
|
+
trust in the warn channel.
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
claims = find_action_claims(response_text)
|
|
150
|
+
if not claims:
|
|
151
|
+
return PhantomActionResult(True, "no-claims")
|
|
152
|
+
tool_uses = count_turn_tool_uses(raw_transcript)
|
|
153
|
+
if tool_uses is None:
|
|
154
|
+
return PhantomActionResult(True, "transcript-unparseable", claims)
|
|
155
|
+
if tool_uses > 0:
|
|
156
|
+
return PhantomActionResult(True, "tools-present", claims)
|
|
157
|
+
return PhantomActionResult(
|
|
158
|
+
False, "phantom-action", claims, _SUGGESTION_TEXT
|
|
159
|
+
)
|
|
160
|
+
except Exception:
|
|
161
|
+
return PhantomActionResult(True, "check-error")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _parse_jsonl(raw: str) -> list[dict]:
|
|
165
|
+
records: list[dict] = []
|
|
166
|
+
for line in raw.splitlines():
|
|
167
|
+
if not line.strip():
|
|
168
|
+
continue
|
|
169
|
+
try:
|
|
170
|
+
record = json.loads(line)
|
|
171
|
+
except json.JSONDecodeError:
|
|
172
|
+
continue
|
|
173
|
+
if isinstance(record, dict):
|
|
174
|
+
records.append(record)
|
|
175
|
+
return records
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _record_message(record: dict) -> dict:
|
|
179
|
+
message = record.get("message")
|
|
180
|
+
return message if isinstance(message, dict) else {}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _record_role(record: dict) -> str:
|
|
184
|
+
role = record.get("role") or _record_message(record).get("role")
|
|
185
|
+
return role if isinstance(role, str) else ""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _record_content(record: dict) -> object:
|
|
189
|
+
content = record.get("content")
|
|
190
|
+
if content is None:
|
|
191
|
+
content = _record_message(record).get("content")
|
|
192
|
+
return content
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _last_real_user_index(records: list[dict]) -> int:
|
|
196
|
+
for idx in range(len(records) - 1, -1, -1):
|
|
197
|
+
if _record_role(records[idx]) != "user":
|
|
198
|
+
continue
|
|
199
|
+
content = _record_content(records[idx])
|
|
200
|
+
if isinstance(content, str):
|
|
201
|
+
return idx
|
|
202
|
+
if isinstance(content, list):
|
|
203
|
+
block_types = {
|
|
204
|
+
block.get("type")
|
|
205
|
+
for block in content
|
|
206
|
+
if isinstance(block, dict)
|
|
207
|
+
}
|
|
208
|
+
if block_types - {"tool_result"}:
|
|
209
|
+
return idx
|
|
210
|
+
return -1
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _count_tool_use_blocks(record: dict) -> int:
|
|
214
|
+
content = _record_content(record)
|
|
215
|
+
if not isinstance(content, list):
|
|
216
|
+
return 0
|
|
217
|
+
return sum(
|
|
218
|
+
1
|
|
219
|
+
for block in content
|
|
220
|
+
if isinstance(block, dict) and block.get("type") == "tool_use"
|
|
221
|
+
)
|
|
@@ -28,6 +28,18 @@ class QGBlocker(BaseModel):
|
|
|
28
28
|
file: str | None = Field(
|
|
29
29
|
default=None, description="File (and line if known) of the issue"
|
|
30
30
|
)
|
|
31
|
+
# Constitution 2.0 (PR-5, 2026-07-08): claim-level verdict vocabulary
|
|
32
|
+
# imported from the frontier review pattern — findings are judged
|
|
33
|
+
# individually, not only the deliverable as a whole.
|
|
34
|
+
verdict: Literal["CONFIRMED", "PLAUSIBLE", "REFUTED"] | None = Field(
|
|
35
|
+
default=None,
|
|
36
|
+
description=(
|
|
37
|
+
"Claim-level verdict after attempting reproduction: CONFIRMED "
|
|
38
|
+
"(reproduced from the code/output), PLAUSIBLE (credible but not "
|
|
39
|
+
"reproduced), REFUTED (disproven — recorded for telemetry, must "
|
|
40
|
+
"NOT count toward rejection)"
|
|
41
|
+
),
|
|
42
|
+
)
|
|
31
43
|
|
|
32
44
|
|
|
33
45
|
class QGEvidenceSummary(BaseModel):
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/core/hooks/stop.py
CHANGED
|
@@ -254,6 +254,28 @@ def _flow_checks(
|
|
|
254
254
|
except Exception:
|
|
255
255
|
pass
|
|
256
256
|
|
|
257
|
+
# Prompt-surface P0 2026-07-08 — phantom-action check (warn-only).
|
|
258
|
+
# Narrated effects without a tool call in the turn = the action did
|
|
259
|
+
# not happen (evidence-flow: evidence, never narration).
|
|
260
|
+
phantom_passed = True
|
|
261
|
+
phantom_reason = "no-claims"
|
|
262
|
+
phantom_claims: list = []
|
|
263
|
+
try:
|
|
264
|
+
from core.governance.phantom_action_check import check_phantom_actions
|
|
265
|
+
pr = check_phantom_actions(last, raw)
|
|
266
|
+
phantom_passed = pr.passed
|
|
267
|
+
phantom_reason = pr.reason
|
|
268
|
+
phantom_claims = pr.claims
|
|
269
|
+
if safe_sid:
|
|
270
|
+
_write_tmp_state("arkaos-phantom", safe_sid, {
|
|
271
|
+
"passed": pr.passed,
|
|
272
|
+
"reason": pr.reason,
|
|
273
|
+
"claims": pr.claims,
|
|
274
|
+
"suggestion": pr.suggestion,
|
|
275
|
+
})
|
|
276
|
+
except Exception:
|
|
277
|
+
pass
|
|
278
|
+
|
|
257
279
|
entry = {
|
|
258
280
|
"ts": datetime.now(timezone.utc).isoformat(),
|
|
259
281
|
"session_id": session_id,
|
|
@@ -277,6 +299,9 @@ def _flow_checks(
|
|
|
277
299
|
"kb_inflated": kb_inflated,
|
|
278
300
|
"closing_marker_check_passed": closing_check_passed,
|
|
279
301
|
"closing_marker_check_reason": closing_check_reason,
|
|
302
|
+
"phantom_check_passed": phantom_passed,
|
|
303
|
+
"phantom_check_reason": phantom_reason,
|
|
304
|
+
"phantom_claims": phantom_claims,
|
|
280
305
|
"effort_level": effort_level,
|
|
281
306
|
"mode": "warn",
|
|
282
307
|
}
|