wicked-garden 12.37.2 → 12.38.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/.claude-plugin/components.json +2 -6
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.claude-plugin/specialist.json +1 -1
- package/CHANGELOG.md +12 -1
- package/docs/adr/0001-code-relationship-graph-engine.md +1 -1
- package/docs/domains.md +1 -16
- package/docs/getting-started.md +2 -2
- package/hooks/scripts/bootstrap.py +3 -28
- package/hooks/scripts/notification.py +8 -27
- package/hooks/scripts/prompt_submit.py +22 -34
- package/package.json +1 -1
- package/scripts/_agents.py +1 -1
- package/scripts/_bus.py +3 -3
- package/scripts/_domain_store.py +2 -2
- package/scripts/_estate_client.py +25 -0
- package/scripts/_logger.py +2 -2
- package/scripts/_session.py +3 -18
- package/scripts/_validate_registry.py +1 -1
- package/scripts/classify/persist.py +1 -1
- package/scripts/crew/specialist_resolver.py +1 -1
- package/scripts/jam/agentic_cli_registry.py +1 -1
- package/scripts/jam/detect_clis.py +1 -1
- package/scripts/platform/observability/assert_contracts.py +2 -2
- package/scripts/reset.py +1 -3
- package/skills/core/SKILL.md +4 -6
- package/skills/core/refs/reset.md +5 -6
- package/skills/engineering/architecture/refs/architecture-template-design.md +2 -2
- package/skills/engineering/debugging/refs/process.md +2 -2
- package/skills/engineering/system-design/refs/component-template-structure.md +2 -2
- package/skills/engineering/system-design/refs/interface-template-maintenance.md +2 -2
- package/skills/engineering/system-design/refs/interface-template-structure.md +2 -2
- package/skills/governed-worker/SKILL.md +2 -2
- package/skills/platform/observability/SKILL.md +0 -1
- package/skills/platform/prereq-doctor/refs/tool-registry.md +1 -1
- package/skills/qe-mutation-test-engineer/SKILL.md +5 -10
- package/skills/qe-observability-test-engineer/SKILL.md +2 -9
- package/skills/qe-production-quality-engineer/SKILL.md +2 -9
- package/skills/qe-release-readiness-engineer/SKILL.md +2 -9
- package/skills/qe-requirements-quality-analyst/SKILL.md +2 -9
- package/skills/qe-risk-assessor/SKILL.md +2 -9
- package/skills/qe-scenario-executor/SKILL.md +3 -10
- package/skills/qe-security-test-engineer/SKILL.md +5 -31
- package/skills/qe-semantic-reviewer/SKILL.md +2 -11
- package/skills/qe-snapshot-hygiene-auditor/SKILL.md +2 -19
- package/skills/qe-test-automation-engineer/SKILL.md +2 -9
- package/skills/qe-test-code-quality-auditor/SKILL.md +2 -20
- package/skills/qe-test-data-manager/SKILL.md +2 -9
- package/skills/qe-test-designer/SKILL.md +2 -9
- package/skills/qe-test-impact-analyzer/SKILL.md +2 -9
- package/skills/qe-test-oracle/SKILL.md +2 -9
- package/skills/qe-test-strategist/SKILL.md +2 -9
- package/skills/qe-testability-reviewer/SKILL.md +2 -9
- package/skills/qe-ui-component-test-engineer/SKILL.md +2 -9
- package/skills/qe-visual-regression-engineer/SKILL.md +5 -10
- package/scripts/_capability_registry.py +0 -334
- package/scripts/_capability_resolver.py +0 -322
- package/scripts/smaht/adapters/__init__.py +0 -83
- package/scripts/smaht/adapters/brain_adapter.py +0 -240
- package/scripts/smaht/adapters/context7_adapter.py +0 -438
- package/scripts/smaht/adapters/delegation_adapter.py +0 -118
- package/scripts/smaht/adapters/domain_adapter.py +0 -206
- package/scripts/smaht/adapters/events_adapter.py +0 -231
- package/scripts/smaht/adapters/tools_adapter.py +0 -50
- package/scripts/smaht/cheatsheet_store.py +0 -236
- package/scripts/smaht/context_package.py +0 -386
- package/scripts/smaht/knowledge_graph.py +0 -395
- package/scripts/smaht/propose_skills.py +0 -856
- package/scripts/smaht/test_cheatsheet_store.py +0 -428
- package/skills/crew-implementer/SKILL.md +0 -7
- package/skills/crew-researcher/SKILL.md +0 -7
- package/skills/crew-reviewer/SKILL.md +0 -7
- package/skills/smaht/SKILL.md +0 -103
- package/skills/smaht/discovery/SKILL.md +0 -70
- package/skills/smaht/intent/SKILL.md +0 -101
- package/skills/smaht/propose-skills/SKILL.md +0 -148
- package/skills/smaht/refs/briefing.md +0 -201
- package/skills/smaht/refs/events-import.md +0 -97
- package/skills/smaht/refs/state.md +0 -82
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Issue #328: Component declarations for wicked-garden plugin. Claude Code plugin.json does not yet support inline component declarations beyond path overrides. This manifest documents all components for tooling, validation, and future schema support. When plugin.json gains a 'components' field, merge this data into plugin.json. The plugin is skills-only: former commands/ became actions of consolidated per-domain skills, and former agents/ became standalone context:fork worker skills (enumerated under fork_skills). Regenerate derived blocks with scripts/ci/sync_components.py.",
|
|
3
|
-
"domains": ["agentic", "crew", "data", "engineering", "jam", "mem", "persona", "platform", "product", "qe", "search"
|
|
3
|
+
"domains": ["agentic", "crew", "data", "engineering", "jam", "mem", "persona", "platform", "product", "qe", "search"],
|
|
4
4
|
"summary": {
|
|
5
|
-
"skills":
|
|
5
|
+
"skills": 133,
|
|
6
6
|
"fork_skills": 69,
|
|
7
7
|
"hooks": 13,
|
|
8
8
|
"specialists": 7
|
|
@@ -15,9 +15,6 @@
|
|
|
15
15
|
"archetype": ["archetype"],
|
|
16
16
|
"classify": ["classify"],
|
|
17
17
|
"core": ["core"],
|
|
18
|
-
"crew-implementer": ["crew-implementer"],
|
|
19
|
-
"crew-researcher": ["crew-researcher"],
|
|
20
|
-
"crew-reviewer": ["crew-reviewer"],
|
|
21
18
|
"data": ["data"],
|
|
22
19
|
"data-engineer": ["data-engineer"],
|
|
23
20
|
"deliberate": ["deliberate"],
|
|
@@ -93,7 +90,6 @@
|
|
|
93
90
|
"qe-visual-regression-engineer": ["qe-visual-regression-engineer"],
|
|
94
91
|
"repo-learn": ["repo-learn"],
|
|
95
92
|
"search": ["codebase-narrator", "search"],
|
|
96
|
-
"smaht": ["discovery", "intent", "propose-skills", "smaht"],
|
|
97
93
|
"swarm": ["swarm"],
|
|
98
94
|
"wickedizer": ["wickedizer"],
|
|
99
95
|
"workflow": ["workflow"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name": "wicked-garden", "owner": {"name": "Mike Parcewski"}, "plugins": [{"name": "wicked-garden", "source": "./", "description": "Your coding agent already plans and swarms \u2014 Wicked Garden is the curated toolkit for what it can't: proof-not-claims gates, code links grep can't see, deterministic refactor, cross-session memory, real multi-model second opinions. Don't fight the harness; fill its gaps. The gate needs wicked-vault (a self-contained infra peer, installed directly) and re-derives through garden's built-in engine; estate/bus are opt-in layers (wicked-testing and wicked-brain are retired \u2014 QE ships in-catalog as the qe domain, memory lives in wicked-estate).", "version": "12.
|
|
1
|
+
{"name": "wicked-garden", "owner": {"name": "Mike Parcewski"}, "plugins": [{"name": "wicked-garden", "source": "./", "description": "Your coding agent already plans and swarms \u2014 Wicked Garden is the curated toolkit for what it can't: proof-not-claims gates, code links grep can't see, deterministic refactor, cross-session memory, real multi-model second opinions. Don't fight the harness; fill its gaps. The gate needs wicked-vault (a self-contained infra peer, installed directly) and re-derives through garden's built-in engine; estate/bus are opt-in layers (wicked-testing and wicked-brain are retired \u2014 QE ships in-catalog as the qe domain, memory lives in wicked-estate).", "version": "12.38.0"}], "version": "12.38.0"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-garden",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.38.0",
|
|
4
4
|
"description": "Your coding agent's harness already plans and swarms; Wicked Garden is the curated toolkit for what it can't do alone. Proof, not claims \u2014 gates re-derive 'done' through the internal loom engine (fail-closed, independently attested via wicked-vault), so a self-asserted pass can't lie its way green. Relationships grep can't see \u2014 the wicked-estate code graph + injected bus/dispatch/capability/archetype edges power blast-radius & lineage. Plus deterministic multi-file refactor (wicked-patch), cross-session memory + knowledge (the mem domain over wicked-estate), real multi-model second opinions (jam:council), portable expertise as on-demand skill-refs, and evidence-gated testing (the in-catalog qe domain). It reads the shape of your work to apply the right rigor \u2014 steering, not a pipeline \u2014 then gets out of the way. The compiler stamps a self-contained evidence gate into any repo that runs with no wicked-garden installed. Don't fight the harness; fill its gaps. The evidence gate requires wicked-vault, a self-contained infra peer wicked-garden installs directly; wicked-bus is an opt-in layer \u2014 install what you'll use, the toolkit works without the rest.",
|
|
5
5
|
"wicked_vault_version": "^0.5.0",
|
|
6
6
|
"wicked_ledger_version": "^0.4.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"plugin": "wicked-garden",
|
|
3
|
-
"_comment": "Specialist roles listed here are user-facing disciplines surfaced by crew for phase routing. Internal infrastructure domains (crew, mem, persona,
|
|
3
|
+
"_comment": "Specialist roles listed here are user-facing disciplines surfaced by crew for phase routing. Internal infrastructure domains (crew, mem, persona, search, jam) are NOT listed as specialists — they are invoked directly via their consolidated domain skills, not routed through the specialist dispatch system. Specialist roles resolve to context:fork worker skills (skills/<domain>-<role>/SKILL.md) via scripts/crew/specialist_resolver.py.",
|
|
4
4
|
"specialists": [
|
|
5
5
|
{
|
|
6
6
|
"name": "engineering",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [12.38.0] — 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **Wave-2 catalog batch B16 (DES-L6 B14–B17; third of the 40 `qe-*` workers): 10 qe specialist workers (`qe-mutation-test-engineer`, `qe-observability-test-engineer`, `qe-production-quality-engineer`, `qe-release-readiness-engineer`, `qe-requirements-quality-analyst`, `qe-risk-assessor`, `qe-scenario-executor`, `qe-security-test-engineer`, `qe-semantic-reviewer`, `qe-snapshot-hygiene-auditor`) go cross-CLI (fixall L6-B16; D-21 — BC-37 / BC-39).** Each drops its Claude-only frontmatter for the closed key set with `metadata.role: worker` and loses the fork sentence; `qe-security-test-engineer` (1777 → 1015) and `qe-snapshot-hygiene-auditor` (1070 → 621) descriptions are trimmed under 1024 (example blocks dropped, security's two NOT/advisor lists condensed). `qe-scenario-executor`'s "Use the Read tool to read the scenario file" reads "Open the scenario file with your file reader". The `qe-mutation-test-engineer` `VERDICT={PASS|FAIL} REVIEWER=… RUN_ID=…` output footer becomes the D-9 grammar on separate lines and leaves `verdict_spelling.legacy_footer_files` (2 → 1, leaving only `qe-visual-regression-engineer` for B17 — the verdict-spelling test fixture target, whose `legacy_footer_files` entry and `_LEGACY_FOOTER` fixture rows move together in B17). `tests/cross_cli_baseline.json` shrinks by exactly this batch (13 pairs across 10 files → 18; 0 stale; nothing added); `components.json` unchanged. BM-style check for the wave-2 P2b/BM re-run: a non-claude seat receives the 10 workers in `skillsSnapshotHanded` with 0 `seatConstraint`, and a mutation run writes `VERDICT: PASS|FAIL` on its own line.
|
|
9
|
+
- **Wave-2 catalog batch B17 (DES-L6 B14–B17; the LAST 10 of the 40 `qe-*` workers): `qe-test-automation-engineer`, `qe-test-code-quality-auditor`, `qe-test-data-manager`, `qe-test-designer`, `qe-test-impact-analyzer`, `qe-test-oracle`, `qe-test-strategist`, `qe-testability-reviewer`, `qe-ui-component-test-engineer`, `qe-visual-regression-engineer` go cross-CLI — completing all 40 qe workers (fixall L6-B17; D-21 — BC-37 / BC-39).** Each drops its Claude-only frontmatter for the closed key set with `metadata.role: worker` and loses the fork sentence; `qe-test-code-quality-auditor`'s over-long description (1117 → 615) drops its `<example>` block. `qe-visual-regression-engineer`'s `VERDICT={PASS|FAIL} REVIEWER=… RUN_ID=…` output footer becomes the D-9 grammar on separate lines (`VERDICT: PASS|FAIL` / `REVIEWER:` / `RUN_ID:`) and leaves `verdict_spelling.legacy_footer_files` — which is now **EMPTY** (all 40 qe footers normalized across B14–B17). Per the B14 carry-forward, the verdict-spelling test fixture moves WITH the list: `tests/test_skill_portability.py`'s three `_LEGACY_FOOTER` rows keyed to `qe-visual-regression-engineer` and the corpus quiet entry are removed (no legacy file survives to point at), `_LEGACY_FOOTER` becomes a generic example, and a legacy-footer-shaped line is now a verdict-spelling hit in EVERY file. `tests/cross_cli_baseline.json` shrinks by exactly this batch (11 pairs across 10 files → **7**; 0 stale; nothing added) — the remaining 7 entries are non-L6 files (`mem`, `search`, `search/codebase-narrator`, `repo-learn`, `wickedizer`, cleared by their owners / B18). `components.json` unchanged. BM-style check for the wave-2 P2b/BM re-run: a non-claude seat receives the 10 workers in `skillsSnapshotHanded` with 0 `seatConstraint`, and a visual-regression run writes `VERDICT: PASS|FAIL` on its own line. With B17, every `qe-*` worker skill is cross-CLI.
|
|
10
|
+
- **Wave-2 catalog batch B18 (DES-L6 — the final L6 garden lint/guard/cleanup batch): N1 CI-hard-fail when PyYAML is unimportable in a CI context, the `#1157` dispatch-lint bare-identifier gap, the `#1156` capability-router retirement, the retired `crew-*` trio stub removal, and five engineering unpaired-fence refs (fixall L6-B18; D-21 — BC-38).** `scripts/ci/validate.py` gains `ci_yaml_requirement_error()` — in a CI context (`CI` / `GITHUB_ACTIONS`) it now FAILS when PyYAML is unimportable, so the frontmatter guard can never silently fall back to the weaker stdlib approximation if a future edit drops `pip install pyyaml` from a workflow; locally the graceful stdlib fallback stays (N1, the 12.37.1 / 12.37.2 guard-durability follow-up). The `claude-dispatch` / `claude-tool-call` lint (`tests/portability_rules.garden.json`) now also catches a bare CAPITALISED first positional argument followed by more args (`Agent(Explore, prompt=…)`, `Read(FilePath, encoding)`) — the `#1157` lookahead gap; a lone `X(Cap)` prose noun (`recall replaces Grep/Glob/Agent(Explore)`) and the plurals `Task(s)` / `Agent(s)` stay quiet (adversarial rows added; 0 new corpus hits — guard hardening). The Claude-plugin capability-router is retired (`#1156`): `scripts/_capability_resolver.py` (322 lines) and `scripts/_capability_registry.py` (334 lines) are deleted, `hooks/scripts/bootstrap.py` drops SessionStart step 3b and `_resolve_capabilities()`, `scripts/_session.py` drops the unread `resolved_capabilities` field, `.claude/commands/wg-check.md` drops §4b + §12 + the summary-table row, `scripts/ci/find_orphan_agents.py` drops the `_capability_registry` root-allowlist clause, and the ADR 0001 injected-edge table notes the runtime router is retired (estate's `agent → tool` edge reads the `tool-capabilities:` frontmatter directly — no registry import). The resolver failed open and NOTHING read its result, so nothing observable changes; this supersedes the loom-extraction spec's "stays in garden" disposition (which meant "don't migrate to loom", not "never retire"). The three retired `crew-implementer` / `crew-researcher` / `crew-reviewer` module stubs (folded into `wicked-garden-governed-worker` §Creator/Neutral/Evaluator in an earlier batch) are deleted; `governed-worker`'s History block marks the three names `<!-- not-a-skill -->`; `components.json` regenerates (skills 140 → 137). Five `skills/engineering/**/refs/*.md` (architecture-template-design, debugging/process, component-template-structure, interface-template-maintenance, interface-template-structure) promote their outer three-backtick `markdown` wrapper (opener + closer) to a four-backtick fence so a three-backtick inner block nests per CommonMark (delisted from `FENCE_PAIRING_BASELINE`). Archetype role ruling: `skills/archetype/SKILL.md` stays `metadata.role: worker` (system-reminder-triggered, not user-invocable) — no change. `tests/cross_cli_baseline.json` is UNCHANGED (its remaining 7 entries are non-L6 `mem` / `search` / `search/codebase-narrator` / `repo-learn` / `wickedizer` files, cleared by their owners' PRs). Deferred with reasons: the colon-form (`wicked-garden:x:y`) token (54 corpus hits across three semantic categories — slash-invocation, bare skill-name cross-refs, and bus `source_agent` JSON payloads — needs a spec + shrink baseline), `detect_state.py` and description-historical treatment (need specs), four ambiguous-boundary fence refs (`examples-saas-trading`, `event-schemas-best-practices`, `scenario-format`, `qe-incident-to-scenario-synthesizer` — swallowed-sections / pre-wrapper classes, a focused fence follow-up), the crew fixture v3 re-vendor (no v3 upstream), and the two retirements (mem-capture / mem-ingest ride L4's mem PR; smaht is a product PR needing the user's call).
|
|
11
|
+
- **The `smaht` domain is retired whole (fixall L6; BC-77 — APPROVED by the user 2026-09-15).** The pull-model context-assembly domain — its skills (`smaht` router + `briefing`/`state`/`events-import` refs, and the `discovery`/`intent`/`propose-skills` sub-skills) and the `scripts/smaht/**` package (adapters, `context_package`, `knowledge_graph`, `propose_skills`, `cheatsheet_store`) — is deleted; garden drops from 9 domains to 8. **Routing is unchanged:** the session-intent primitive that drives archetype routing (auto-detection in `hooks/scripts/prompt_submit.py::_detect_intent` + the sticky `state.intent`) lives in the hook and `_session.py`, never in smaht — the `smaht-intent` skill was only the manual OVERRIDE writer. Retiring it removes the override path (`state.intent_explicit` and the `<wg intent="X" t=N />` label are dropped; auto-detected intent was already invisible, so the observable directive stream is unchanged) while the UserPromptSubmit hook still classifies every prompt to an archetype. No production code imported `scripts/smaht` (verified); consumers are rewired: the `smaht` entry leaves the domain lists (`_agents.py`, `_validate_registry.py`, `specialist_resolver.py`, `find_orphan_agents.py`, `components.json`, `_relevance_bulkadd.py`), the `reset.py` domain map, and the routing tables in `skills/core/SKILL.md` + `docs/domains.md`; the live `wicked.garden.fact.extracted` bus event (now produced by `scripts/mem/session_fact_extractor.py`) is relabelled off the `smaht` domain; `notification.py`'s dead context-pressure flag writes (whose reader — the v2 orchestrator — was deleted in #428) are removed. The six GENERAL onboarding/install scenarios mis-filed under `scenarios/smaht/` (fresh-install, plugin-degradation, mcp-integration, the two onboarding guards, wizard-environment-scan — 0 smaht refs; they exercise core plugin bootstrap) are RELOCATED to `scenarios/core/` (preserving coverage and re-rooting `wicked-garden-wickedizer`); the 3 genuinely smaht-specific scenarios are deleted. `tests/hooks/test_prompt_submit_intent.py` is the gate: intent auto-detects, no override field/label survives, and archetype routing classifies with smaht gone. `find_orphan_agents` stays 0 orphaned; `validate.py` / registry / sync pass. Cross-lane follow-ups (NOT in this PR): `scenarios/search/lifecycle-scoring.md` (L4-owned) references the deleted `smaht.adapters.domain_adapter` — scenarios are not CI-executed, so it rides L4's cleanup; and `site/src/data/garden.ts` still lists the `smaht` domain (rides the site lane). The `_context_backend` stats folder keeps its legacy `smaht` name (renaming would orphan on-disk stats).
|
|
12
|
+
- **The estate shim scopes capture proposals to the run's studio project (BC-79 reader half; #1172).** `scripts/_estate_client.py` reads `WICKED_RUN_PROJECT` (stamped on the worker env by wicked-core's WRAPPED carrier since core-ts 0.7.29) into `facets.project` before `propose(...)` sends a capture, so an approved capture lands at the run's project scope instead of the state-home default; absent or blank — an older core that does not stamp it, or a non-run invocation — leaves `facets` unchanged, preserving prior behavior. `_scope_facets_to_run_project` never mutates the caller's dict; `tests/test_estate_client.py` covers the set / override / absent / blank paths.
|
|
13
|
+
|
|
5
14
|
## [12.37.2] — 2026-09-15
|
|
6
15
|
|
|
7
16
|
### Changed
|
|
17
|
+
- **Guard upgrade (fast-follow to the 12.37.1 frontmatter-drop incident): the SKILL.md frontmatter guard now runs the real `yaml.safe_load` in CI, not a stdlib approximation (fixall L6-guard-upgrade).** The hotfix's stdlib structural check caught the three observed shapes but a reviewer's adversarial battery found 8 cases the publish's parser REJECTS while the stdlib guard PASSED — most seriously the same frontmatter-drop class under a NESTED `metadata:` / `mandates:` key (the block every wave-2 skill carries), plus tab-indentation, mis-indented nested maps, unterminated quotes, colon-in-unquoted-value, and a col-0 sequence item mixed into the mapping. `scripts/ci/validate.py::frontmatter_yaml_error` now calls `yaml.safe_load` on every frontmatter block — the exact mirror of the skills publish's parser — so nothing the publish would reject goes green in CI, and it still flags the SILENT subclass a plain parse accepts (a bare-token list value absorbed into a `|`/`>` block scalar); a stdlib structural check remains only as a graceful fallback if PyYAML is unimportable. `pip install pyyaml` is added to `.github/workflows/validate.yml`, `test.yml` (alongside pytest) and `release.yml`'s pre-publish `test_cmd` (PyYAML is a declared garden dependency; the legs previously ran bare, which is why the first guard had to be stdlib). `tests/test_validate_frontmatter.py` gains the 8 divergence rows (each now FAILS the guard), a guard-mirrors-`yaml.safe_load` check across the battery, and the live-tree clean assertion.
|
|
8
18
|
- **Hotfix (frontmatter-drop class; ship-blocker for 12.37.1 → cut as 12.37.2): three translated skills had a dropped `tool-capabilities:` key that left its value line(s) ORPHANED in the frontmatter (fixall L6-hotfix).** `skills/data-engineer/SKILL.md` (batch B8) left ` - data-query` after the key was removed, which made the frontmatter INVALID YAML (`expected <block end>, but found '<block sequence start>'`) — `yaml.safe_load` refused it, blocking the ENTIRE skills publish (rig can't republish → `baseSkillPolicy=require` refuses every launch). Two more of the same class were absorbed into a `|` block description (valid YAML, corrupt content, so `yaml.safe_load` did not catch them): `skills/agentic-safety-reviewer/SKILL.md` (B8, ` - security-scanning`) and `skills/platform-security-engineer/SKILL.md` (B6, ` - security-scanning` + ` - version-control`). All orphaned value lines are deleted (the `tool-capabilities` key was intentionally dropped in those batches; its values go with it). **Systemic guard:** `scripts/ci/validate.py` now runs a strict STRUCTURAL frontmatter check on every SKILL.md (stdlib only — garden CI runs the validate/test legs with a bare interpreter, no PyYAML, so it cannot import the publish's `yaml.safe_load`; the check reproduces the one failure mode that matters) and FAILS on an orphaned value line under a scalar key (the syntax break that stopped 12.37.1's publish) AND on a bare-token list value absorbed into a `|` block scalar (the silent subclass); `tests/test_validate_frontmatter.py` carries the adversarial rows (an orphan after a quoted scalar, an orphan absorbed into a `|` block, a nested-map orphan, and missing frontmatter all fail; clean frontmatter, a `|` block, `mandates:` lists, and legitimate prose bullets pass) plus a live-tree assertion. Root cause: the cross-CLI batches' frontmatter rewriter dropped a key's line without its indented value lines, and garden CI read frontmatter with a lenient regex, not a strict YAML parse — so it slipped through fifteen batches. A full repo sweep of all 140 SKILL.md confirms these three were the only affected files.
|
|
9
19
|
- **Wave-2 catalog batch B9 (DES-L6 B11): the `jam` and `persona` domains — two routers, `jam-brainstorm-facilitator` / `jam-council`, three refs — are cross-CLI, and the `persona-agent` worker is RETIRED into `persona/refs/as.md` (fixall L6-B9; D-21 — BC-37 / BC-39).** The routers carry the closed key set with `metadata.role: router` (`jam` phases `clarify,design`; `persona` phases `*`): `jam`'s `brainstorm` and `council` sub-actions hand off to the two worker skills through Hand-off paragraphs carrying the exact asks (the routing table says "Hand-off →", the Workers section drops "forked", council seats are "separate-worker seats"); `persona`'s Source Priority names "the harness's plugin cache" instead of a `.claude/plugins` path. The two jam workers are `role: worker` (Claude hint keys, fork sentences and the pre-v12.25 `Task(subagent_type=…)` compat tails dropped — reached by name through a Hand-off; "dispatched by" → "handed off by" in their descriptions); the facilitator's `TaskCreate(` / `TaskUpdate(` session tracking reads as "track the session in the harness's task list where it has one, else your working notes" with the subject, metadata and appends named, its three `Skill(skill="wicked-garden-mem", …)` recall/store calls are Hand-off paragraphs and its Step 1b names the `wicked-garden-search` skill; the council's "subagent tier" is "the separate-worker tier" (its seat-independence Hand-off was already cross-CLI) and its outcome store is a Hand-off paragraph. `persona-agent` (a 122-line fork worker whose whole job was to run the profile the `as` action assembled) is retired: `persona/refs/as.md` Step 5 "Embody the persona" carries the profile as a ```markdown template and a new "Embodying the persona" section carries the worker's behavioral guidelines, archetype patterns, response format and task-type adaptations verbatim (harness-neutral tool wording); `skills/persona-agent/` is deleted, `components.json` regenerated (`fork_skills` 73 → 72, skills 144 → 143), `docs/domains.md` says so. `jam/refs/revisit.md`'s two mem calls are Hand-off paragraphs; `council-verdict.md` says "worker returns". `tests/test_skill_naming.py` loses the ">= 3 skills keep a `subagent_type` compat key" floor — the batches drop the key on purpose (BC-37), so the drop is not silent; the well-formedness test still runs over the two remaining declarers (`mem-capture`, `mem-ingest`) until B10 retires them. Kept on purpose: the `{topic-slug}` / `{persona}` template placeholders, description trigger phrases → the B18 colon-form token. `tests/cross_cli_baseline.json` shrinks by exactly this batch (20 pairs across 7 files → 119; 0 stale; nothing added — the deleted `persona-agent/SKILL.md` entry goes with the file). BM-style check for the wave-2 P2b/BM re-run: a non-claude seat receives both routers and the two jam workers in `skillsSnapshotHanded` with 0 `seatConstraint`, a jam `brainstorm` unit on that seat hands off to `wicked-garden-jam-brainstorm-facilitator` by name, and a persona `as` unit embodies the persona inline with no `Skill(` in its output.
|
|
10
20
|
- **Wave-2 catalog batch B10 (DES-L6 B12): the fold/retire batch — `integration-discovery` RETIRED, `runtime-exec` folded into `core/refs/runtime.md`, `engineering-conformance-reviewer` folded into `engineering/refs/conformance.md`, and `smaht` (+ `discovery` / `intent` / `propose-skills` / `refs/briefing.md`), `mem-capture` and `mem-ingest` are cross-CLI (fixall L6-B10; D-21 — BC-37 / BC-39).** `wicked-garden-integration-discovery` (a `user-invocable: false`, `status: experimental` capability router nothing dispatched — the `integration-discovery` strings in `_domain_store.py` / `_session.py` / `domain_adapter.py` name the routing mechanism, not the skill) is deleted with its four refs. `wicked-garden-runtime-exec`'s body moves verbatim into `skills/core/refs/runtime.md` minus its frontmatter, H1 and the shared launcher paragraph `core/SKILL.md` already carries, and the router's Runtime section points at it; `wicked-garden-engineering-conformance-reviewer`'s body moves verbatim into `skills/engineering/refs/conformance.md`, a pointer lands at the end of the router's Knowledge modules section, `scripts/platform/guard_pipeline.py` stops telling the operator to invoke a skill that no longer exists, and `docs/outgov-per-turn.md` names the ref. The `smaht` router carries the closed key set with `metadata.role: router` (phases `*`; description 1042 → under 1024 chars — the alias line drops and `wicked-garden:search` reads as the `wicked-garden-search` skill), loses its fork sentence and the dispatch-fallback sentence, and its Quick Reference's two `Skill(skill="wicked-garden-mem", …)` calls become one Hand-off paragraph (the symbol-lookup comment now says "through the estate read-only shim", matching the grounding doctrine that landed in 12.37.1); `discovery` / `intent` / `propose-skills` are `role: module` (discovery's "Fork-worker dispatches (`Skill(skill=…)`, or the legacy `subagent_type=…` compat form)" detection bullets describe Hand-off paragraphs and worker names, propose-skills reads "the harness's session transcripts"), and `refs/briefing.md`'s fallback memory call is a Hand-off paragraph. `mem-capture` and `mem-ingest` get the closed key set with `metadata.role: worker` — bodies untouched. **Scope deviation from DES B12, disclosed:** the DES says those two workers and `smaht` RETIRE "— `mem/SKILL.md` untouched", but both clauses cannot hold — the L4-owned `skills/mem/SKILL.md` dispatches `wicked-garden-mem-ingest` and `wicked-garden-mem-capture` by name at four sites (and `mem/refs/scopes.md` at one), so deleting them dangles five identifiers in files this lane does not own and `unresolved-skill-name` fails loud on exactly that (B9's guard); `wicked-garden-smaht-intent` is a live user-override path in `hooks/scripts/prompt_submit.py` and `scripts/_session.py`, `core/SKILL.md` lists smaht in two routing tables, and `docs/domains.md` counts it among the 9 domains. Both are translated here and their retirement is owed as a follow-up that carries the L4 router edit (mem) and the hook/docs changes (smaht) in the same PR. `tests/cross_cli_baseline.json` shrinks by exactly this batch (26 pairs across 10 files → 93; 0 stale; nothing added — the deleted files' entries go with them); `.claude-plugin/components.json` regenerated (`fork_skills` 72 → 69 — `engineering-conformance-reviewer` and `integration-discovery` leave with their deleted files and `smaht` leaves the set because it is now `metadata.role: router` instead of a `context: fork`-inferred worker; `runtime-exec` was already a module); `scripts/wg/_relevance_bulkadd.py` drops the two rows for the deleted dirs and the `portability_rules.garden.json` corpus entry pinned to the retired `runtime-exec` file is re-pointed at its new home, so no corpus row names a deleted file. BM-style check for the wave-2 P2b/BM re-run: a non-claude seat receives `wicked-garden-smaht`, `wicked-garden-mem-capture` and `wicked-garden-mem-ingest` in `skillsSnapshotHanded` with 0 `seatConstraint` and none of the three retired names, and a smaht `briefing` unit on that seat hands off to `wicked-garden-mem` by name with no `Skill(` in its output.
|
|
@@ -918,7 +928,8 @@ through `v9.2.18`. Major themes across that span:
|
|
|
918
928
|
|
|
919
929
|
For each of those releases, see the corresponding tag in git history.
|
|
920
930
|
|
|
921
|
-
[Unreleased]: https://github.com/mikeparcewski/wicked-garden/compare/v12.
|
|
931
|
+
[Unreleased]: https://github.com/mikeparcewski/wicked-garden/compare/v12.38.0...HEAD
|
|
932
|
+
[12.38.0]: https://github.com/mikeparcewski/wicked-garden/compare/v12.37.2...v12.38.0
|
|
922
933
|
[12.37.2]: https://github.com/mikeparcewski/wicked-garden/compare/v12.37.1...v12.37.2
|
|
923
934
|
[12.37.1]: https://github.com/mikeparcewski/wicked-garden/compare/v12.37.0...v12.37.1
|
|
924
935
|
[12.37.0]: https://github.com/mikeparcewski/wicked-garden/compare/v12.36.0...v12.37.0
|
|
@@ -30,7 +30,7 @@ reference, so neither `grep` nor a static call-graph can see them:
|
|
|
30
30
|
|---|---|
|
|
31
31
|
| event producer → consumer | `emit_event("wicked.x")` + `_bus_consumers.json` `event_filter` |
|
|
32
32
|
| command/Task → agent | `subagent_type:` frontmatter |
|
|
33
|
-
| agent → tool | `tool-capabilities`
|
|
33
|
+
| agent → tool | `tool-capabilities` frontmatter (the `_capability_registry` runtime router was retired 2026-09, #1156) |
|
|
34
34
|
| hook event → script | `hooks.json` |
|
|
35
35
|
| archetype → playbook/gate | `archetypes.json` + `refs/` |
|
|
36
36
|
|
package/docs/domains.md
CHANGED
|
@@ -6,7 +6,7 @@ Work is shaped by the **archetypes** (triage, explore, specify, decide, ship, re
|
|
|
6
6
|
|
|
7
7
|
The plugin is **skills-only** (v12.25). Each domain is one consolidated, user-invokable skill — `skills/{domain}/SKILL.md`, frontmatter name `wicked-garden-{domain}` — that routes to its **actions** (the former `/wicked-garden:{domain}:{command}` surfaces) and loads rubrics on demand from `skills/{domain}/refs/` and nested knowledge modules. The former dispatch agents are **fork worker skills** — `skills/{domain}-{role}/SKILL.md` with `context: fork`, frontmatter name `wicked-garden-{domain}-{role}` — reached by `Skill()`/`Task()` dispatch, never invoked as entry points. Invoke a domain action as `/wicked-garden-{domain} {action} [args]` (or just describe what you want — the skill descriptions carry the triggers).
|
|
8
8
|
|
|
9
|
-
There are **
|
|
9
|
+
There are **8 domains**: engineering, platform, product, data, jam, search, agentic, persona.
|
|
10
10
|
|
|
11
11
|
> **v12 cleanup (ADR 0002).** Most former domain commands were *rubric-wrappers* — a checklist the agent already applies. These were **collapsed**: the rubric moved to an on-demand `skills/{domain}/refs/{name}.md` and the action now loads it and works **inline** (no `Task` dispatch hop). Dispatch is kept only where it earns it — real parallelism (multiple lenses at once), a real external tool, or an independent gate. The dispatch-only agents that nothing reaches anymore were removed; workers still referenced by a surviving action, skill, scenario, or the specialist registry live on as fork skills. Capability is preserved; only the token-burning hop is gone.
|
|
12
12
|
|
|
@@ -161,21 +161,6 @@ They do not overlap; pick by target:
|
|
|
161
161
|
|
|
162
162
|
**Worker**: none — the former `wicked-garden-persona-agent` worker is retired into `refs/as.md` (wave-2 B9): the `as` action builds the profile from the registry at runtime and embodies it inline. `define` runs inline from `refs/define.md`; the no-op `persona:submit` stub was deleted.
|
|
163
163
|
|
|
164
|
-
## smaht — Context Assembly
|
|
165
|
-
|
|
166
|
-
`wicked-garden-smaht` — on-demand context assembly over the wicked-estate knowledge layer and the search index. A pull-model skill — archetypes and subagents call it when they need a briefing, rather than pushing context onto every prompt.
|
|
167
|
-
|
|
168
|
-
| Action | What It Does |
|
|
169
|
-
|--------|-------------|
|
|
170
|
-
| `briefing` | What happened since the last session — recent events and updates |
|
|
171
|
-
| `state` | Snapshot and report current session state |
|
|
172
|
-
| `events-import` | Import existing domain JSON records into the event log |
|
|
173
|
-
| `intent` | Set or inspect the active session intent (nested `intent` sub-skill) |
|
|
174
|
-
|
|
175
|
-
> `briefing` and `events-import` are real event-store tools and stay; `state` runs inline from `refs/state.md` (its dead v6 half dropped). Nested sub-skills: `discovery`, `intent`, `propose-skills`.
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
164
|
## How archetypes invoke domains
|
|
180
165
|
|
|
181
166
|
An archetype playbook (`skills/archetype/refs/{archetype}.md`) doesn't hardcode a domain pipeline — it reaches for whatever expertise the work needs:
|
package/docs/getting-started.md
CHANGED
|
@@ -77,7 +77,7 @@ All ten are available: `triage`, `explore`, `specify`, `decide`, `ship`, `review
|
|
|
77
77
|
|
|
78
78
|
### 3. Use a Domain Skill Directly
|
|
79
79
|
|
|
80
|
-
Archetypes invoke the domain skills (engineering, platform, product, data, jam, search, agentic, persona
|
|
80
|
+
Archetypes invoke the domain skills (engineering, platform, product, data, jam, search, agentic, persona) under the hood, but you can call them directly when you want a single focused pass. Each domain is one skill with routed actions:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
/wicked-garden-engineering review # senior-perspective code review
|
|
@@ -193,7 +193,7 @@ To list everything:
|
|
|
193
193
|
|
|
194
194
|
When you submit a prompt, a `UserPromptSubmit` hook runs the archetype detector and emits a steering reminder for the matched work-shape(s). The selected archetype then drives its own phase shape — there is no universal pipeline forcing every kind of work through the same gates.
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
Every `TaskCreate` / `TaskUpdate` carries a structured metadata envelope (`chain_id`, `event_type`, `source_agent`, `phase`, `archetype`) validated by a PreToolUse hook, and a SubagentStart hook injects the matching procedure bundle — R1–R6 bulletproof standards for coding tasks, the Gate Finding Protocol for review findings, and per-role procedures otherwise.
|
|
197
197
|
|
|
198
198
|
Gates re-derive every claim through wicked-loom — which re-runs the verifier via wicked-vault — and fail closed if loom is unavailable: "done" is *re-derived from evidence*, never self-asserted. Hard gates (incident mitigate, migrate cutover, review final-verdict) require explicit human approval; discrete gates auto-pass only when their produces contract is met.
|
|
199
199
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
SessionStart hook — wicked-garden unified bootstrap.
|
|
4
4
|
|
|
5
|
-
Consolidates: crew session_start, mem session_start,
|
|
6
|
-
search session_start, smaht session_start.
|
|
5
|
+
Consolidates: crew session_start, mem session_start, search session_start.
|
|
7
6
|
|
|
8
7
|
Flow:
|
|
9
8
|
1. Read config from ~/.something-wicked/wicked-garden/config.json
|
|
@@ -305,22 +304,6 @@ def _load_agents():
|
|
|
305
304
|
return 0, {}
|
|
306
305
|
|
|
307
306
|
|
|
308
|
-
def _resolve_capabilities(agents, config=None):
|
|
309
|
-
"""Run capability resolution for pre-loaded agents.
|
|
310
|
-
|
|
311
|
-
Returns dict[agent_name, tool_list] or empty dict on any error.
|
|
312
|
-
Fails open — never blocks bootstrap.
|
|
313
|
-
"""
|
|
314
|
-
try:
|
|
315
|
-
from _capability_resolver import resolve_all_agents, discover_mcp_servers
|
|
316
|
-
|
|
317
|
-
mcp_servers = discover_mcp_servers()
|
|
318
|
-
return resolve_all_agents(agents, config, mcp_servers)
|
|
319
|
-
except Exception as e:
|
|
320
|
-
print(f"[wicked-garden] capability resolution error: {e}", file=sys.stderr)
|
|
321
|
-
return {}
|
|
322
|
-
|
|
323
|
-
|
|
324
307
|
def _find_active_crew_project(workspace: str = ""):
|
|
325
308
|
"""Return (project_data, project_name) for most recently updated active crew project
|
|
326
309
|
scoped to the given workspace.
|
|
@@ -1306,11 +1289,6 @@ def main():
|
|
|
1306
1289
|
if state is not None:
|
|
1307
1290
|
state.update(agents_loaded=agents_loaded)
|
|
1308
1291
|
|
|
1309
|
-
# 3b. Resolve tool-capabilities for agents that declare them
|
|
1310
|
-
resolutions = _resolve_capabilities(agents_dict, config)
|
|
1311
|
-
if resolutions and state is not None:
|
|
1312
|
-
state.update(resolved_capabilities=resolutions)
|
|
1313
|
-
|
|
1314
1292
|
# 4. Load last crew project for this workspace
|
|
1315
1293
|
workspace = os.environ.get("CLAUDE_PROJECT_NAME") or Path.cwd().name
|
|
1316
1294
|
project_data, project_name = _find_active_crew_project(workspace)
|
|
@@ -1455,11 +1433,8 @@ def main():
|
|
|
1455
1433
|
is_active = active_phase not in ("", "complete", "done", "archived")
|
|
1456
1434
|
state.update(active_project_id=project_name if is_active else None)
|
|
1457
1435
|
|
|
1458
|
-
# v9.2.4: also write active_chain_id so
|
|
1459
|
-
#
|
|
1460
|
-
# works. Field declared in v9.2.3, producer was missing — events
|
|
1461
|
-
# got the 0.1 baseline instead of the documented 0.8+ chain
|
|
1462
|
-
# boost. Format mirrors the chain_id convention in CLAUDE.md
|
|
1436
|
+
# v9.2.4: also write active_chain_id so chain-aware event
|
|
1437
|
+
# scoring works. Format mirrors the chain_id convention in CLAUDE.md
|
|
1463
1438
|
# "Native Tasks as Dual-Purpose Event Queue":
|
|
1464
1439
|
# {slug}.root for the root chain
|
|
1465
1440
|
# {slug}.{phase} per active phase
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Notification hook — wicked-garden context limit adaptation.
|
|
4
4
|
|
|
5
|
-
Issue #343: Detect context limit warnings and
|
|
5
|
+
Issue #343: Detect context limit warnings and warn Claude.
|
|
6
6
|
|
|
7
7
|
When Claude Code emits notifications about approaching context limits,
|
|
8
|
-
this hook
|
|
9
|
-
-
|
|
10
|
-
- Reduce context assembly scope
|
|
8
|
+
this hook returns a systemMessage nudging Claude to:
|
|
9
|
+
- Minimize context — keep only what the current step needs
|
|
11
10
|
- Encourage delegation over inline execution
|
|
12
11
|
- Log the context pressure event for observability
|
|
13
12
|
|
|
@@ -80,32 +79,14 @@ def _is_context_limit_notification(payload: dict) -> bool:
|
|
|
80
79
|
# ---------------------------------------------------------------------------
|
|
81
80
|
|
|
82
81
|
def _handle_context_limit(payload: dict) -> str:
|
|
83
|
-
"""
|
|
82
|
+
"""Warn Claude when context limits are approaching.
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- context_pressure_since: ISO timestamp
|
|
88
|
-
- force_delegation: True — encourages delegation over inline work
|
|
89
|
-
|
|
90
|
-
Returns a systemMessage with guidance for Claude.
|
|
84
|
+
Returns a systemMessage with guidance for Claude (prefer delegation,
|
|
85
|
+
keep responses concise, save critical context before compaction).
|
|
91
86
|
"""
|
|
92
87
|
_log("notification", "warn", "context.limit_approaching",
|
|
93
88
|
detail={"payload_type": payload.get("type", ""), "message": (payload.get("message", "") or "")[:100]})
|
|
94
89
|
|
|
95
|
-
# Update session state
|
|
96
|
-
try:
|
|
97
|
-
from _session import SessionState
|
|
98
|
-
from datetime import datetime, timezone
|
|
99
|
-
|
|
100
|
-
state = SessionState.load()
|
|
101
|
-
state.update(
|
|
102
|
-
context_pressure=True,
|
|
103
|
-
context_pressure_since=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
104
|
-
force_delegation=True,
|
|
105
|
-
)
|
|
106
|
-
except Exception:
|
|
107
|
-
pass
|
|
108
|
-
|
|
109
90
|
# Name the memory surface (estate memory.capture). Fail-open to the
|
|
110
91
|
# same wording.
|
|
111
92
|
try:
|
|
@@ -117,8 +98,8 @@ def _handle_context_limit(payload: dict) -> str:
|
|
|
117
98
|
return json.dumps({
|
|
118
99
|
"systemMessage": (
|
|
119
100
|
"[Context Pressure] Context limit is approaching. Adapting behavior:\n"
|
|
120
|
-
"1.
|
|
121
|
-
"2. Prefer delegation to subagents
|
|
101
|
+
"1. Minimize context — keep only what the current step needs\n"
|
|
102
|
+
"2. Prefer delegation to subagents over inline execution\n"
|
|
122
103
|
"3. Keep responses concise — avoid large code dumps\n"
|
|
123
104
|
"4. If in a crew project, consider completing the current phase before starting new work\n"
|
|
124
105
|
f"5. Use {_mem_target} to save critical context before compaction"
|
|
@@ -607,7 +607,7 @@ def _check_onboarding_gate(prompt: str) -> str | None:
|
|
|
607
607
|
# ---------------------------------------------------------------------------
|
|
608
608
|
# The intent variable names what five overlapping classifiers were collectively
|
|
609
609
|
# detecting. Auto-detected on turn 1-2 from existing complexity/risk signals,
|
|
610
|
-
# sticky for the session
|
|
610
|
+
# sticky for the session (auto-detected from the first turn's prompt).
|
|
611
611
|
# Hooks gate directive emission on intent. Design: brainstorms/v10-session-01-intent-and-hook-gating.md.
|
|
612
612
|
|
|
613
613
|
_INTENT_VALUES = ("simple-edit", "feature", "rigor", "research")
|
|
@@ -637,15 +637,13 @@ def _detect_intent(prompt: str, complexity: float, is_risky: bool, state) -> str
|
|
|
637
637
|
"""Auto-detect session intent from prompt + complexity/risk signals.
|
|
638
638
|
|
|
639
639
|
Called once per session on turn 1-2. Result is sticky — subsequent turns
|
|
640
|
-
read state.intent directly.
|
|
641
|
-
skill, which sets state.intent_explicit=True.
|
|
640
|
+
read state.intent directly.
|
|
642
641
|
|
|
643
642
|
Detection priority:
|
|
644
643
|
1. Rigor invocations (wicked-garden-crew* skills, /wg-issue, /wg-test) → rigor
|
|
645
644
|
2. Research signals (explain how, why does, walk me through) → research
|
|
646
645
|
3. Risk OR complexity ≥ threshold OR technical verb → feature
|
|
647
|
-
4. Default → simple-edit (conservative
|
|
648
|
-
wicked-garden-smaht-intent skill)
|
|
646
|
+
4. Default → simple-edit (conservative)
|
|
649
647
|
"""
|
|
650
648
|
if not prompt or not prompt.strip():
|
|
651
649
|
return "simple-edit"
|
|
@@ -679,7 +677,7 @@ def _ensure_intent_set(prompt: str, state, complexity: float, is_risky: bool) ->
|
|
|
679
677
|
``state.intent`` for a turn. On turn ≤ _AUTO_DETECT_TURN_LIMIT and
|
|
680
678
|
intent is None → detect via ``_detect_intent``, persist via
|
|
681
679
|
``state.update``, return. On later turns or when intent is already
|
|
682
|
-
set → return existing value.
|
|
680
|
+
set → return existing value.
|
|
683
681
|
|
|
684
682
|
Returns the effective intent value (always a string from _INTENT_VALUES).
|
|
685
683
|
"""
|
|
@@ -696,30 +694,26 @@ def _ensure_intent_set(prompt: str, state, complexity: float, is_risky: bool) ->
|
|
|
696
694
|
detected = _detect_intent(prompt, complexity, is_risky, state)
|
|
697
695
|
try:
|
|
698
696
|
if state:
|
|
699
|
-
state.update(intent=detected
|
|
697
|
+
state.update(intent=detected)
|
|
700
698
|
except Exception:
|
|
701
699
|
pass # fail open — return value still usable for this turn
|
|
702
700
|
return detected
|
|
703
701
|
|
|
704
702
|
|
|
705
|
-
def _build_intent_directive(intent: str, turn_count: int,
|
|
703
|
+
def _build_intent_directive(intent: str, turn_count: int, state=None) -> str:
|
|
706
704
|
"""Build the system-reminder directive for a given intent.
|
|
707
705
|
|
|
708
706
|
Output rules (brainstorm decision):
|
|
709
|
-
simple-edit
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
The label-only-on-explicit rule prevents confirmation-bias drift —
|
|
716
|
-
auto-detected intent stays invisible to the model so it doesn't
|
|
707
|
+
simple-edit → empty string (silence is the signal)
|
|
708
|
+
feature → synthesis directive
|
|
709
|
+
research → synthesis directive
|
|
710
|
+
rigor → synthesis directive + chain context
|
|
711
|
+
|
|
712
|
+
Auto-detected intent stays invisible to the model so it doesn't
|
|
717
713
|
second-guess the framework's silent classification.
|
|
718
714
|
"""
|
|
719
|
-
label = f'<wg intent="{intent}" t={turn_count} />' if explicit else ""
|
|
720
|
-
|
|
721
715
|
if intent == "simple-edit":
|
|
722
|
-
return
|
|
716
|
+
return "" # silence is the signal
|
|
723
717
|
|
|
724
718
|
# synthesis directive shared by feature / research / rigor.
|
|
725
719
|
# Fail-open to a matching inline copy if the backend module cannot be
|
|
@@ -748,10 +742,7 @@ def _build_intent_directive(intent: str, turn_count: int, explicit: bool, state=
|
|
|
748
742
|
)
|
|
749
743
|
directive_lines.append("Only after grounding is complete, answer the original prompt.")
|
|
750
744
|
|
|
751
|
-
|
|
752
|
-
if label:
|
|
753
|
-
return f"{label}\n{directive}"
|
|
754
|
-
return directive
|
|
745
|
+
return "\n".join(directive_lines)
|
|
755
746
|
|
|
756
747
|
|
|
757
748
|
# ---------------------------------------------------------------------------
|
|
@@ -793,7 +784,7 @@ def _build_wip_recovery_block(session_id: str, project: str) -> str:
|
|
|
793
784
|
# (current_task, decisions, file_scope, active_constraints, open_questions)
|
|
794
785
|
# are gone. If nothing is in-progress, emit nothing.
|
|
795
786
|
if not in_progress_tasks:
|
|
796
|
-
_log("
|
|
787
|
+
_log("prompt", "debug", "wip_recovery.empty")
|
|
797
788
|
return ""
|
|
798
789
|
|
|
799
790
|
parts = ["## [Recovery] Work in progress\n"]
|
|
@@ -808,7 +799,7 @@ def _build_wip_recovery_block(session_id: str, project: str) -> str:
|
|
|
808
799
|
if len(block) > 1000:
|
|
809
800
|
block = block[:997] + "..."
|
|
810
801
|
|
|
811
|
-
_log("
|
|
802
|
+
_log("prompt", "debug", "wip_recovery.done", detail={"chars": len(block)})
|
|
812
803
|
return block
|
|
813
804
|
|
|
814
805
|
|
|
@@ -818,7 +809,7 @@ def _build_wip_recovery_block(session_id: str, project: str) -> str:
|
|
|
818
809
|
|
|
819
810
|
def main():
|
|
820
811
|
_t0 = time.monotonic()
|
|
821
|
-
_log("
|
|
812
|
+
_log("prompt", "debug", "hook.start")
|
|
822
813
|
|
|
823
814
|
try:
|
|
824
815
|
raw = sys.stdin.read()
|
|
@@ -938,7 +929,6 @@ def main():
|
|
|
938
929
|
_log("prompt", "debug", "intent.resolved",
|
|
939
930
|
detail={
|
|
940
931
|
"intent": intent,
|
|
941
|
-
"explicit": bool(getattr(state, "intent_explicit", False)),
|
|
942
932
|
"complexity": round(complexity, 2),
|
|
943
933
|
"risk": is_risky,
|
|
944
934
|
"turn": turn_count,
|
|
@@ -950,12 +940,10 @@ def main():
|
|
|
950
940
|
# block runs from the onboarding-pending branch where no intent was
|
|
951
941
|
# computed) drives directive emission. Auto-detected simple-edit
|
|
952
942
|
# turns produce an empty directive; feature/research/rigor produce
|
|
953
|
-
# the synthesis directive.
|
|
954
|
-
# bare label so the model knows the user steered the framework.
|
|
943
|
+
# the synthesis directive.
|
|
955
944
|
_intent = getattr(state, "intent", None) if state else None
|
|
956
|
-
_intent_explicit = bool(getattr(state, "intent_explicit", False)) if state else False
|
|
957
945
|
intent_directive = _build_intent_directive(
|
|
958
|
-
_intent or "simple-edit", turn_count,
|
|
946
|
+
_intent or "simple-edit", turn_count, state
|
|
959
947
|
)
|
|
960
948
|
|
|
961
949
|
# WIP recovery: surface native in-progress tasks (v5 ticket rail gone)
|
|
@@ -1024,8 +1012,8 @@ def main():
|
|
|
1024
1012
|
|
|
1025
1013
|
merged_context = f"<system-reminder>\n{chr(10).join(all_parts)}\n</system-reminder>"
|
|
1026
1014
|
|
|
1027
|
-
_log("
|
|
1028
|
-
detail={"intent": _intent, "
|
|
1015
|
+
_log("prompt", "debug", "prompt.intent_directive",
|
|
1016
|
+
detail={"intent": _intent, "turn": turn_count,
|
|
1029
1017
|
"directive_bytes": len(intent_directive.encode("utf-8"))})
|
|
1030
1018
|
|
|
1031
1019
|
output = {
|
|
@@ -1035,7 +1023,7 @@ def main():
|
|
|
1035
1023
|
},
|
|
1036
1024
|
"continue": True,
|
|
1037
1025
|
}
|
|
1038
|
-
_log("
|
|
1026
|
+
_log("prompt", "debug", "hook.end", ms=int((time.monotonic() - _t0) * 1000))
|
|
1039
1027
|
print(json.dumps(output))
|
|
1040
1028
|
|
|
1041
1029
|
except Exception as e:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-garden",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.38.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Curated toolkit for what AI coding agents can't do alone: evidence-gated work, codegraph relationships, deterministic multi-file refactor, multi-model second opinions, and cross-session memory. Installs as a Claude Code plugin.",
|
|
6
6
|
"keywords": [
|
package/scripts/_agents.py
CHANGED
|
@@ -367,7 +367,7 @@ def _parse_simple_yaml(text: str) -> dict[str, Any]:
|
|
|
367
367
|
|
|
368
368
|
# Known plugin domains (mirrors .claude-plugin/components.json "domains")
|
|
369
369
|
_DOMAINS = ("agentic", "crew", "data", "engineering", "jam", "mem", "persona",
|
|
370
|
-
"platform", "product", "qe", "search"
|
|
370
|
+
"platform", "product", "qe", "search")
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
def _infer_domain(path: Path) -> str:
|
package/scripts/_bus.py
CHANGED
|
@@ -290,9 +290,9 @@ BUS_EVENT_MAP: Dict[str, Dict[str, str]] = {
|
|
|
290
290
|
"subdomain": "crew.yolo",
|
|
291
291
|
"description": "Yolo auto-approval revoked due to scope-increase mutation (audit + observability)",
|
|
292
292
|
},
|
|
293
|
-
#
|
|
293
|
+
# Garden fact stream — scripts/mem/session_fact_extractor.py → auto-memorize subscriber
|
|
294
294
|
"wicked.garden.fact.extracted": {
|
|
295
|
-
"domain": "
|
|
295
|
+
"domain": "wicked-garden",
|
|
296
296
|
"subdomain": "facts",
|
|
297
297
|
"description": "Structured fact extracted from conversation (consumed by the garden-run auto-memorize drain -> estate memory)",
|
|
298
298
|
},
|
|
@@ -799,7 +799,7 @@ def _resolve_bus_db_path() -> Optional[str]:
|
|
|
799
799
|
def tail_events(limit: int = 10) -> List[Dict[str, Any]]:
|
|
800
800
|
"""Return the most recent ``limit`` bus events, newest first. Read-only.
|
|
801
801
|
|
|
802
|
-
This is a *display* helper (e.g.
|
|
802
|
+
This is a *display* helper (e.g. a session-state inspection). It does
|
|
803
803
|
NOT register a subscriber, advance a cursor, or mutate the bus in any way:
|
|
804
804
|
the wicked-bus ``replay`` command resets a cursor rather than returning
|
|
805
805
|
rows, and ``list`` returns subscribers, so neither yields event rows
|
package/scripts/_domain_store.py
CHANGED
|
@@ -21,7 +21,7 @@ Usage:
|
|
|
21
21
|
|
|
22
22
|
# For truly ephemeral files (caches, temp session state) only:
|
|
23
23
|
from _domain_store import get_local_path
|
|
24
|
-
cache_dir = get_local_path("wicked-
|
|
24
|
+
cache_dir = get_local_path("wicked-crew", "cache", "sessions")
|
|
25
25
|
|
|
26
26
|
hook_mode=True:
|
|
27
27
|
When True, skips integration-discovery entirely and operates local-only.
|
|
@@ -71,7 +71,7 @@ DOMAIN_MCP_PATTERNS: dict[str, list[str]] = {
|
|
|
71
71
|
],
|
|
72
72
|
"wicked-crew": ["jira", "linear", "github", "rally", "ado", "azure-devops", "project"],
|
|
73
73
|
"wicked-garden:search": ["elasticsearch", "algolia", "typesense", "search"],
|
|
74
|
-
# wicked-
|
|
74
|
+
# wicked-qe, wicked-patch: local-only (no MCP patterns)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
|
|
@@ -187,6 +187,11 @@ READONLY_ENV = "WICKED_ESTATE_READONLY" # explicit read-only opt-in outsid
|
|
|
187
187
|
STORE_PIN_ENV = ("WICKED_ESTATE_DB", "WICKED_HOME", "WICKED_MEMORY_DB")
|
|
188
188
|
READONLY_FLAG = "--readonly" # exact token — the MCP matches it literally
|
|
189
189
|
DB_FLAG = "--db"
|
|
190
|
+
# The run's studio project id, stamped on the worker env by wicked-core's WRAPPED
|
|
191
|
+
# carrier since core-ts 0.7.29 (BC-79). When set, it is the authoritative scope for
|
|
192
|
+
# capture proposals (`facets.project`), overriding whatever the worker self-derived;
|
|
193
|
+
# absent/blank (older cores, or a non-run invocation) it changes nothing.
|
|
194
|
+
RUN_PROJECT_ENV = "WICKED_RUN_PROJECT"
|
|
190
195
|
|
|
191
196
|
_readonly_flag: bool = False # set by --readonly / set_readonly()
|
|
192
197
|
_governed_flag: bool = False # set by --governed / set_governed()
|
|
@@ -879,6 +884,25 @@ def knowledge_recall(query: str, token_budget: int = 2000, timeout: float = 8.0)
|
|
|
879
884
|
return call("knowledge.recall", {"query": query, "token_budget": token_budget}, timeout=timeout)
|
|
880
885
|
|
|
881
886
|
|
|
887
|
+
def _scope_facets_to_run_project(facets: Optional[dict]) -> Optional[dict]:
|
|
888
|
+
"""Scope a proposal to the run's studio project (BC-79, reader half).
|
|
889
|
+
|
|
890
|
+
When the governed run stamps `WICKED_RUN_PROJECT` (the run's project id, set on
|
|
891
|
+
the worker env by wicked-core's WRAPPED carrier since core-ts 0.7.29), that id is
|
|
892
|
+
authoritative: it becomes `facets.project`, overriding whatever the worker
|
|
893
|
+
self-derived, so approved captures land at the run's project scope instead of the
|
|
894
|
+
state-home default. Absent or blank (an older core that does not stamp it, or a
|
|
895
|
+
non-run invocation) → `facets` is returned UNCHANGED, preserving today's behavior
|
|
896
|
+
(the worker's own `facets.project`, or none). The caller's dict is never mutated.
|
|
897
|
+
"""
|
|
898
|
+
run_project = os.environ.get(RUN_PROJECT_ENV, "").strip()
|
|
899
|
+
if not run_project:
|
|
900
|
+
return facets
|
|
901
|
+
scoped = dict(facets) if facets else {}
|
|
902
|
+
scoped["project"] = run_project
|
|
903
|
+
return scoped
|
|
904
|
+
|
|
905
|
+
|
|
882
906
|
def propose(
|
|
883
907
|
kind_type: str,
|
|
884
908
|
payload: dict,
|
|
@@ -894,6 +918,7 @@ def propose(
|
|
|
894
918
|
objects in the deliverable file / report instead). `provenance` is NEVER sent:
|
|
895
919
|
the server stamps it from its own `WICKED_RUN_*` environment.
|
|
896
920
|
"""
|
|
921
|
+
facets = _scope_facets_to_run_project(facets)
|
|
897
922
|
arguments: dict = {"kind_type": kind_type, "payload": payload}
|
|
898
923
|
if facets:
|
|
899
924
|
arguments["facets"] = facets
|
package/scripts/_logger.py
CHANGED
|
@@ -9,7 +9,7 @@ Usage:
|
|
|
9
9
|
from _logger import log
|
|
10
10
|
|
|
11
11
|
log("bootstrap", "normal", "onboarding.status", ok=True, detail={"has_memories": True})
|
|
12
|
-
log("
|
|
12
|
+
log("prompt", "verbose", "intent.resolved", detail={"intent": "feature", "turn": 1})
|
|
13
13
|
log("post_tool", "debug", "hook.end", ms=1.23)
|
|
14
14
|
|
|
15
15
|
Level hierarchy: normal (0) < verbose (1) < debug (2)
|
|
@@ -94,7 +94,7 @@ def log(
|
|
|
94
94
|
"""Write a structured log entry to the session ops JSONL file.
|
|
95
95
|
|
|
96
96
|
Args:
|
|
97
|
-
domain: Source domain, e.g. "bootstrap", "
|
|
97
|
+
domain: Source domain, e.g. "bootstrap", "prompt", "crew"
|
|
98
98
|
level: Log level — "normal", "verbose", or "debug"
|
|
99
99
|
event: Dot-separated event name, e.g. "onboarding.status"
|
|
100
100
|
ok: Outcome indicator (default True)
|