arkaos 4.22.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/THE-ARKAOS-GUIDE.md +72 -0
- package/VERSION +1 -1
- package/arka/SKILL.md +28 -6
- 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/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 +38 -11
- package/core/governance/stop_lint_telemetry.py +11 -2
- package/core/hooks/user_prompt_submit.py +21 -23
- 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 +93 -30
- package/core/synapse/layers_base.py +16 -0
- package/core/synapse/layers_kb.py +325 -19
- package/dashboard/app/layouts/default.vue +7 -0
- package/dashboard/app/pages/plan-canvas.vue +630 -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 +3 -2
- package/departments/kb/skills/doc-redaction/SKILL.md +3 -2
- 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 +8 -1
- package/installer/keys.js +18 -3
- package/installer/update.js +8 -1
- package/knowledge/skills-manifest.json +1 -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,72 @@
|
|
|
1
|
+
# The ArkaOS Guide
|
|
2
|
+
|
|
3
|
+
> v4.23.0 — 86 agents, 17 departments, 292 skills, 270 commands, 16 ADRs.
|
|
4
|
+
> One file, everything you need to start. Generated by `scripts/guide_gen.py` — never hand-edited.
|
|
5
|
+
|
|
6
|
+
## What it is
|
|
7
|
+
|
|
8
|
+
ArkaOS is an operating system for AI agent teams: specialized
|
|
9
|
+
departments with framework-backed agents, a constitution with a
|
|
10
|
+
Quality Gate that holds veto power, and an evidence flow where
|
|
11
|
+
gates pass on tool output read from disk — never on narration.
|
|
12
|
+
|
|
13
|
+
## Start here
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx arkaos install # detects your runtime, installs the core
|
|
17
|
+
npx arkaos doctor # canonical health check (venv, hooks,
|
|
18
|
+
# fastpath, security advisory)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then inside your runtime: `/arka setup` (profile) and `/do <what
|
|
22
|
+
you need>` — plain language routes to the right department.
|
|
23
|
+
|
|
24
|
+
## Keep it updated (two steps)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx arkaos@latest update # step 1: core, hooks, CLI
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then `/arka update` inside the runtime — step 2 syncs every
|
|
31
|
+
project (skills, MCP configs, settings, stack rules).
|
|
32
|
+
|
|
33
|
+
## Departments
|
|
34
|
+
|
|
35
|
+
Every request routes through a department squad — ArkaOS never
|
|
36
|
+
answers as a generic assistant.
|
|
37
|
+
|
|
38
|
+
| Prefix | Commands |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `/arka` | 22 |
|
|
41
|
+
| `/brand` | 12 |
|
|
42
|
+
| `/community` | 14 |
|
|
43
|
+
| `/content` | 19 |
|
|
44
|
+
| `/dev` | 36 |
|
|
45
|
+
| `/do` | 1 |
|
|
46
|
+
| `/ecom` | 18 |
|
|
47
|
+
| `/fin` | 10 |
|
|
48
|
+
| `/kb` | 22 |
|
|
49
|
+
| `/landing` | 14 |
|
|
50
|
+
| `/lead` | 10 |
|
|
51
|
+
| `/mkt` | 12 |
|
|
52
|
+
| `/ops` | 24 |
|
|
53
|
+
| `/org` | 10 |
|
|
54
|
+
| `/pm` | 12 |
|
|
55
|
+
| `/saas` | 14 |
|
|
56
|
+
| `/sales` | 10 |
|
|
57
|
+
| `/strat` | 10 |
|
|
58
|
+
|
|
59
|
+
## How work ships
|
|
60
|
+
|
|
61
|
+
1. **G1 CONTEXT** — routing + grounding before anything runs.
|
|
62
|
+
2. **G2 PLAN** — explicit approval before code.
|
|
63
|
+
3. **G3 EXECUTE** — real test runs with exit codes on record.
|
|
64
|
+
4. **G4 REVIEW** — the Quality Gate (CQO + copy + tech reviewers)
|
|
65
|
+
issues a binary APPROVED/REJECTED; failing evidence can never
|
|
66
|
+
be overridden by narrative.
|
|
67
|
+
|
|
68
|
+
## Go deeper
|
|
69
|
+
|
|
70
|
+
- Wiki: https://github.com/andreagroferreira/arka-os/wiki
|
|
71
|
+
- npm: https://www.npmjs.com/package/arkaos
|
|
72
|
+
- Dashboard: `npx arkaos dashboard` (localhost:3333)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.23.0
|
package/arka/SKILL.md
CHANGED
|
@@ -7,20 +7,42 @@ description: >
|
|
|
7
7
|
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## KB-First Research (
|
|
10
|
+
## KB-First Research (`always-research` SHOULD)
|
|
11
11
|
|
|
12
12
|
Canonical home of the doctrine — every skill's compact
|
|
13
|
-
`arka:kb-first-prefix` pointer references this section.
|
|
13
|
+
`arka:kb-first-prefix` pointer references this section. The Knowledge Base
|
|
14
|
+
has two co-equal faces over the same corpus: **Obsidian** (note content)
|
|
15
|
+
and **Graphify** (the knowledge graph — how concepts connect). Before any
|
|
14
16
|
external research (Context7, WebSearch, WebFetch, Firecrawl):
|
|
15
17
|
|
|
16
18
|
1. Call `mcp__obsidian__search_notes` on the query first.
|
|
17
|
-
2.
|
|
18
|
-
|
|
19
|
+
2. When Graphify is configured, also call `mcp__graphify__query_graph`
|
|
20
|
+
(or `god_nodes` / `shortest_path`) for the relationship view.
|
|
21
|
+
3. Cite relevant hits with `[[wikilinks]]` and/or graph nodes, or
|
|
22
|
+
explicitly declare a KB gap.
|
|
23
|
+
4. Only after (1)–(3) may external tools run.
|
|
24
|
+
|
|
25
|
+
**Fail-open:** Graphify runs on a per-user endpoint (home LAN, localhost,
|
|
26
|
+
or VPS — see `knowledge.graphify` config). When it is not configured or
|
|
27
|
+
not reachable, treat it as a gap and proceed with Obsidian alone; never
|
|
28
|
+
block a turn waiting on it. Obsidian remains the mandatory first step.
|
|
19
29
|
|
|
20
30
|
The Synapse L2.5 layer pre-injects top KB matches on every user prompt;
|
|
21
31
|
treat them as your default source. External research supplements the vault;
|
|
22
|
-
it does not replace it.
|
|
23
|
-
|
|
32
|
+
it does not replace it.
|
|
33
|
+
|
|
34
|
+
**Enforcement** runs on three surfaces, in the order they fire:
|
|
35
|
+
|
|
36
|
+
| Surface | Effect |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| PreToolUse gate (`core/workflow/research_gate.py`) | DENIES the tool call in the current turn on a repeat violation |
|
|
39
|
+
| Quality Gate | DOD item `kb-research-cited` (`definition_of_done.universal`) is `hard: true` — a failed hard item blocks the done marker (rule `definition-of-done-per-domain`) |
|
|
40
|
+
| Stop hook (`core/governance/kb_cite_check.py`) | SOFT — measures citation compliance and nudges the next turn, never blocks this one |
|
|
41
|
+
|
|
42
|
+
Governing rule: `always-research` (SHOULD, `config/constitution.yaml`).
|
|
43
|
+
`kb-research-cited` is a Definition-of-Done item (`hard: true`), not an
|
|
44
|
+
entry in the must/should/non-negotiable rule lists — the two are
|
|
45
|
+
complementary, not alternatives.
|
|
24
46
|
|
|
25
47
|
# ArkaOS — Main Orchestrator
|
|
26
48
|
|
|
@@ -16,8 +16,9 @@ allowed-tools: [Agent, Read, Write, Bash]
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
<!-- arka:kb-first-prefix begin -->
|
|
19
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
20
|
-
> `[[wikilinks]]`
|
|
19
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
20
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
21
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
21
22
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
22
23
|
<!-- arka:kb-first-prefix end -->
|
|
23
24
|
|
|
@@ -18,8 +18,9 @@ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
<!-- arka:kb-first-prefix begin -->
|
|
21
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
22
|
-
> `[[wikilinks]]`
|
|
21
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
22
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
23
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
23
24
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
24
25
|
<!-- arka:kb-first-prefix end -->
|
|
25
26
|
|
|
@@ -19,8 +19,9 @@ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
<!-- arka:kb-first-prefix begin -->
|
|
22
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
23
|
-
> `[[wikilinks]]`
|
|
22
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
23
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
24
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
24
25
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
25
26
|
<!-- arka:kb-first-prefix end -->
|
|
26
27
|
|
|
@@ -14,8 +14,9 @@ allowed-tools: [Read, Bash]
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
<!-- arka:kb-first-prefix begin -->
|
|
17
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
18
|
-
> `[[wikilinks]]`
|
|
17
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
18
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
19
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
19
20
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
20
21
|
<!-- arka:kb-first-prefix end -->
|
|
21
22
|
|
|
@@ -15,8 +15,9 @@ allowed-tools: [Read, AskUserQuestion, Skill]
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
<!-- arka:kb-first-prefix begin -->
|
|
18
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
19
|
-
> `[[wikilinks]]`
|
|
18
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
19
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
20
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
20
21
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
21
22
|
<!-- arka:kb-first-prefix end -->
|
|
22
23
|
|
|
@@ -17,8 +17,9 @@ allowed-tools: [Agent, Read, Write, mcp__obsidian__search_notes]
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
19
|
<!-- arka:kb-first-prefix begin -->
|
|
20
|
-
> **KB-first:** query `mcp__obsidian__search_notes` and
|
|
21
|
-
> `[[wikilinks]]`
|
|
20
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
21
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
22
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
22
23
|
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
23
24
|
<!-- arka:kb-first-prefix end -->
|
|
24
25
|
|
package/bin/arka-doctor
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
# ARKA OS — Doctor (Health Check System)
|
|
4
4
|
# Usage: arka doctor [--fix] [--json]
|
|
5
5
|
# 18 modular checks: fail = critical, warn = advisory
|
|
6
|
+
#
|
|
7
|
+
# SCOPE: this doctor audits the legacy v1 layout (~/.arka-os +
|
|
8
|
+
# ~/.claude/skills). The CANONICAL doctor for the engine (venv, hooks,
|
|
9
|
+
# fastpath, security advisory) is `npx arkaos doctor` — see issue #358
|
|
10
|
+
# for the consolidation/migration map.
|
|
6
11
|
# ============================================================================
|
|
7
12
|
set -euo pipefail
|
|
8
13
|
|
|
@@ -421,4 +426,7 @@ else
|
|
|
421
426
|
echo -e " ${G}All checks passed!${N} ARKA OS is healthy."
|
|
422
427
|
fi
|
|
423
428
|
echo ""
|
|
429
|
+
echo -e " ${C}Note:${N} this audits the legacy v1 layout (~/.arka-os + ~/.claude/skills)."
|
|
430
|
+
echo -e " For the engine (venv, hooks, fastpath, security advisory) run: ${C}npx arkaos doctor${N}"
|
|
431
|
+
echo ""
|
|
424
432
|
fi
|
|
@@ -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
|
+
)
|
|
@@ -23,6 +23,8 @@ CLI (for hooks/skills)::
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
25
|
import argparse
|
|
26
|
+
import configparser
|
|
27
|
+
import fnmatch
|
|
26
28
|
import importlib.util
|
|
27
29
|
import json
|
|
28
30
|
import re
|
|
@@ -31,7 +33,7 @@ import shutil
|
|
|
31
33
|
import subprocess
|
|
32
34
|
import sys
|
|
33
35
|
import time
|
|
34
|
-
from dataclasses import asdict, dataclass, field
|
|
36
|
+
from dataclasses import asdict, dataclass, field, replace
|
|
35
37
|
from pathlib import Path
|
|
36
38
|
|
|
37
39
|
from core.shared.test_evidence import coverage_percent_from_xml
|
|
@@ -218,18 +220,22 @@ def _labelled(result: CheckResult, label: str) -> CheckResult:
|
|
|
218
220
|
return result
|
|
219
221
|
|
|
220
222
|
|
|
221
|
-
def _tool_cmd(tool: str) -> list[str] | None:
|
|
223
|
+
def _tool_cmd(tool: str, module: str | None = None) -> list[str] | None:
|
|
222
224
|
"""Resolve a Python tool: PATH binary, else the interpreter's module.
|
|
223
225
|
|
|
224
226
|
Operator machines install ruff/pytest into the ArkaOS venv with no
|
|
225
227
|
PATH binary — keying on shutil.which alone silently downgraded
|
|
226
228
|
Python lint to eslint over installer/*.js, a FALSE GREEN on a
|
|
227
229
|
NON-NEGOTIABLE gate (QG findings, F1-B2/F1-C1 reviews).
|
|
230
|
+
|
|
231
|
+
``module`` covers tools whose import name differs from the command
|
|
232
|
+
(codespell ships ``codespell_lib``); without it the module fallback
|
|
233
|
+
misses and the check skips forever on a venv install.
|
|
228
234
|
"""
|
|
229
235
|
if shutil.which(tool):
|
|
230
236
|
return [tool]
|
|
231
|
-
if importlib.util.find_spec(tool) is not None:
|
|
232
|
-
return [sys.executable, "-m", tool]
|
|
237
|
+
if importlib.util.find_spec(module or tool) is not None:
|
|
238
|
+
return [sys.executable, "-m", module or tool]
|
|
233
239
|
return None
|
|
234
240
|
|
|
235
241
|
|
|
@@ -335,15 +341,59 @@ def _check_typecheck(
|
|
|
335
341
|
return _skip("typecheck", "no typecheck configuration detected")
|
|
336
342
|
|
|
337
343
|
|
|
344
|
+
def _project_pytest(project_dir: Path) -> list[str] | None:
|
|
345
|
+
"""pytest belonging to the target project's own environment."""
|
|
346
|
+
for rel in (".venv/bin/pytest", "venv/bin/pytest"):
|
|
347
|
+
candidate = project_dir / rel
|
|
348
|
+
if candidate.is_file():
|
|
349
|
+
return [str(candidate)]
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _foreign_pytest_can_collect(
|
|
354
|
+
pytest_cmd: list[str], project_dir: Path, timeout: int,
|
|
355
|
+
) -> bool:
|
|
356
|
+
"""A PATH/venv-foreign pytest must prove it can import the project.
|
|
357
|
+
|
|
358
|
+
Running a foreign-env pytest blind produced false FAILs on projects
|
|
359
|
+
whose dependencies live in their own venv (import errors read as
|
|
360
|
+
test failures — issue #283, a blind gate on mandatory-qa). Exit 5
|
|
361
|
+
("no tests collected") still counts as a working import path.
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
probe = subprocess.run(
|
|
365
|
+
[*pytest_cmd, "--collect-only", "-q"], cwd=project_dir,
|
|
366
|
+
capture_output=True, text=True, timeout=min(timeout, 60),
|
|
367
|
+
)
|
|
368
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
369
|
+
return False
|
|
370
|
+
return probe.returncode in (0, 5)
|
|
371
|
+
|
|
372
|
+
|
|
338
373
|
def _check_tests(
|
|
339
374
|
project_dir: Path, changed: list[str] | None,
|
|
340
375
|
test_command: str | None, timeout: int,
|
|
341
376
|
) -> CheckResult:
|
|
342
377
|
if test_command:
|
|
343
378
|
return _run("tests", shlex.split(test_command), project_dir, timeout)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
379
|
+
if _has_python(project_dir, changed):
|
|
380
|
+
local_pytest = _project_pytest(project_dir)
|
|
381
|
+
if local_pytest:
|
|
382
|
+
return _labelled(
|
|
383
|
+
_run("tests", [*local_pytest, "-q"], project_dir, timeout),
|
|
384
|
+
"tests(project-venv)",
|
|
385
|
+
)
|
|
386
|
+
pytest_cmd = _tool_cmd("pytest")
|
|
387
|
+
if pytest_cmd and _foreign_pytest_can_collect(
|
|
388
|
+
pytest_cmd, project_dir, timeout
|
|
389
|
+
):
|
|
390
|
+
return _run("tests", [*pytest_cmd, "-q"], project_dir, timeout)
|
|
391
|
+
if pytest_cmd:
|
|
392
|
+
return _skip(
|
|
393
|
+
"tests",
|
|
394
|
+
"pytest resolved outside the project env cannot import "
|
|
395
|
+
"it — pin --test-command",
|
|
396
|
+
)
|
|
347
397
|
if _package_json_script(project_dir, "test"):
|
|
348
398
|
return _run(
|
|
349
399
|
"tests", ["npm", "test", "--silent"], project_dir, timeout,
|
|
@@ -501,12 +551,54 @@ def _check_spellcheck(
|
|
|
501
551
|
project_dir: Path, changed: list[str] | None,
|
|
502
552
|
test_command: str | None, timeout: int,
|
|
503
553
|
) -> CheckResult:
|
|
504
|
-
|
|
554
|
+
# Resolve like lint/tests do. A bare shutil.which lookup left this check
|
|
555
|
+
# dark on venv installs (the binary is not on the harness PATH), so three
|
|
556
|
+
# consecutive QG rounds shipped with NO machine spellcheck signal.
|
|
557
|
+
cmd = _tool_cmd("codespell", module="codespell_lib")
|
|
558
|
+
if cmd is None:
|
|
505
559
|
return _skip("spellcheck", "codespell not installed")
|
|
506
560
|
md_files = [f for f in changed or [] if f.endswith(".md")]
|
|
507
561
|
if not md_files:
|
|
508
562
|
return _skip("spellcheck", "no changed .md files")
|
|
509
|
-
|
|
563
|
+
result = _run("spellcheck", [*cmd, *md_files], project_dir, timeout)
|
|
564
|
+
# codespell honours .codespellrc `skip` even for explicitly listed files,
|
|
565
|
+
# so a PASS here can cover far fewer files than were handed in. Reporting
|
|
566
|
+
# the raw count as coverage is how a narrowed scope reads as a full green
|
|
567
|
+
# (QG finding: 8 of 220 inspected, reported as 220). State both.
|
|
568
|
+
inspected = _spellcheck_inspected_count(project_dir, md_files)
|
|
569
|
+
scope = f"{inspected} of {len(md_files)} changed .md inspected"
|
|
570
|
+
if inspected < len(md_files):
|
|
571
|
+
scope += f"; {len(md_files) - inspected} excluded by .codespellrc skip"
|
|
572
|
+
summary = f"{scope}\n{result.summary}" if result.summary else scope
|
|
573
|
+
return replace(result, summary=summary)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _spellcheck_inspected_count(project_dir: Path, md_files: list[str]) -> int:
|
|
577
|
+
"""How many of ``md_files`` codespell actually reads after `skip`.
|
|
578
|
+
|
|
579
|
+
Asks codespell itself (``--count`` on a per-file basis is too slow; a
|
|
580
|
+
skipped file simply produces no output for any planted probe), so instead
|
|
581
|
+
we replay its own glob semantics from the config.
|
|
582
|
+
"""
|
|
583
|
+
patterns = _codespell_skip_globs(project_dir)
|
|
584
|
+
if not patterns:
|
|
585
|
+
return len(md_files)
|
|
586
|
+
return sum(
|
|
587
|
+
1 for f in md_files
|
|
588
|
+
if not any(fnmatch.fnmatch(f, p) or fnmatch.fnmatch(f"./{f}", p) for p in patterns)
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _codespell_skip_globs(project_dir: Path) -> list[str]:
|
|
593
|
+
"""`skip` globs from .codespellrc, or [] when unreadable/absent."""
|
|
594
|
+
cfg = project_dir / ".codespellrc"
|
|
595
|
+
try:
|
|
596
|
+
parser = configparser.ConfigParser()
|
|
597
|
+
parser.read(cfg, encoding="utf-8")
|
|
598
|
+
raw = parser.get("codespell", "skip", fallback="")
|
|
599
|
+
except (OSError, configparser.Error):
|
|
600
|
+
return []
|
|
601
|
+
return [p.strip() for p in raw.split(",") if p.strip()]
|
|
510
602
|
|
|
511
603
|
|
|
512
604
|
def _check_ui_screenshot(
|