wicked-garden 12.27.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/archetypes.json +166 -0
- package/.claude-plugin/components.json +75 -0
- package/.claude-plugin/marketplace.json +15 -0
- package/.claude-plugin/plugin.json +174 -0
- package/.claude-plugin/specialist.json +48 -0
- package/.claude-plugin/stack-registry.json +96 -0
- package/AGENTS.md +13 -0
- package/CHANGELOG.md +712 -0
- package/CONTRIBUTING.md +99 -0
- package/ETHOS.md +83 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/WICKED_GARDEN_BUS_EVENTS.md +149 -0
- package/agents/agentic/architect.md +449 -0
- package/agents/agentic/performance-analyst.md +702 -0
- package/agents/agentic/safety-reviewer.md +662 -0
- package/agents/crew/implementer.md +163 -0
- package/agents/crew/researcher.md +94 -0
- package/agents/crew/reviewer.md +184 -0
- package/agents/data/data-engineer.md +227 -0
- package/agents/engineering/api-documentarian.md +440 -0
- package/agents/engineering/migration-engineer.md +301 -0
- package/agents/engineering/solution-architect.md +208 -0
- package/agents/jam/brainstorm-facilitator.md +297 -0
- package/agents/jam/council.md +322 -0
- package/agents/jam/quick-facilitator.md +85 -0
- package/agents/persona/persona-agent.md +121 -0
- package/agents/platform/compliance-officer.md +266 -0
- package/agents/platform/privacy-expert.md +496 -0
- package/agents/platform/security-engineer.md +287 -0
- package/agents/product/a11y-expert.md +323 -0
- package/agents/product/requirements-analyst.md +238 -0
- package/agents/product/ui-reviewer.md +180 -0
- package/agents/product/ux-designer.md +199 -0
- package/agents/product/value-strategist.md +269 -0
- package/agents/qe/semantic-reviewer.md +78 -0
- package/commands/agentic/audit.md +24 -0
- package/commands/agentic/design.md +24 -0
- package/commands/agentic/frameworks.md +25 -0
- package/commands/agentic/review.md +27 -0
- package/commands/archetype/build.md +12 -0
- package/commands/archetype/decide.md +12 -0
- package/commands/archetype/explore.md +12 -0
- package/commands/archetype/incident.md +12 -0
- package/commands/archetype/migrate.md +12 -0
- package/commands/archetype/modernize.md +12 -0
- package/commands/archetype/review.md +14 -0
- package/commands/archetype/ship.md +12 -0
- package/commands/archetype/specify.md +12 -0
- package/commands/archetype/triage.md +12 -0
- package/commands/compile.md +37 -0
- package/commands/data/analyze.md +18 -0
- package/commands/data/data.md +21 -0
- package/commands/data/ml.md +18 -0
- package/commands/data/ontology.md +22 -0
- package/commands/data/pipeline.md +17 -0
- package/commands/deliberate.md +181 -0
- package/commands/engineering/add-field.md +77 -0
- package/commands/engineering/apply.md +58 -0
- package/commands/engineering/arch.md +26 -0
- package/commands/engineering/debug.md +27 -0
- package/commands/engineering/docs.md +27 -0
- package/commands/engineering/new-generator.md +257 -0
- package/commands/engineering/patch-plan.md +59 -0
- package/commands/engineering/plan.md +29 -0
- package/commands/engineering/remove.md +48 -0
- package/commands/engineering/rename.md +44 -0
- package/commands/engineering/review.md +32 -0
- package/commands/help.md +128 -0
- package/commands/install.md +118 -0
- package/commands/intent.md +54 -0
- package/commands/jam/brainstorm.md +37 -0
- package/commands/jam/council.md +49 -0
- package/commands/jam/quick.md +21 -0
- package/commands/jam/revisit.md +79 -0
- package/commands/persona/as.md +136 -0
- package/commands/persona/define.md +24 -0
- package/commands/persona/list.md +118 -0
- package/commands/platform/actions.md +20 -0
- package/commands/platform/assert.md +18 -0
- package/commands/platform/audit.md +23 -0
- package/commands/platform/compliance.md +20 -0
- package/commands/platform/gh.md +22 -0
- package/commands/platform/health.md +22 -0
- package/commands/platform/incident.md +23 -0
- package/commands/platform/infra.md +24 -0
- package/commands/platform/peer-health.md +103 -0
- package/commands/platform/plugin-health.md +58 -0
- package/commands/platform/security.md +38 -0
- package/commands/platform/toolchain.md +96 -0
- package/commands/platform/traces.md +20 -0
- package/commands/product/a11y.md +19 -0
- package/commands/product/acceptance.md +19 -0
- package/commands/product/align.md +20 -0
- package/commands/product/analyze.md +23 -0
- package/commands/product/elicit.md +19 -0
- package/commands/product/listen.md +24 -0
- package/commands/product/mockup.md +20 -0
- package/commands/product/screenshot.md +19 -0
- package/commands/product/strategy.md +40 -0
- package/commands/product/synthesize.md +23 -0
- package/commands/product/ux-review.md +42 -0
- package/commands/product/ux.md +20 -0
- package/commands/product/visual-direction.md +34 -0
- package/commands/prove.md +47 -0
- package/commands/report-issue.md +243 -0
- package/commands/reset.md +144 -0
- package/commands/search/blast-radius.md +58 -0
- package/commands/search/hotspots.md +55 -0
- package/commands/search/index.md +38 -0
- package/commands/search/lineage.md +47 -0
- package/commands/search/service-map.md +79 -0
- package/commands/setup.md +363 -0
- package/commands/smaht/briefing.md +200 -0
- package/commands/smaht/events-import.md +95 -0
- package/commands/smaht/state.md +19 -0
- package/commands/where-am-i.md +58 -0
- package/docs/README.md +32 -0
- package/docs/adr/0001-code-relationship-graph-engine.md +116 -0
- package/docs/adr/0002-major-cleanup-keep-cut.md +91 -0
- package/docs/adr/0003-peers-gate-required-layers-optin.md +41 -0
- package/docs/adr/0004-code-graph-moves-to-wicked-brain.md +47 -0
- package/docs/brain-chunk-format.md +135 -0
- package/docs/compiler.md +95 -0
- package/docs/daemon-api.md +70 -0
- package/docs/domains.md +197 -0
- package/docs/getting-started.md +208 -0
- package/docs/plans/2026-06-08-wicked-loom-conduct.md +1337 -0
- package/docs/plans/2026-06-08-wicked-loom-contract.md +638 -0
- package/docs/plans/2026-06-08-wicked-loom-cutover.md +1300 -0
- package/docs/plans/2026-06-08-wicked-loom-expand-compose.md +843 -0
- package/docs/plans/2026-06-08-wicked-loom-flow-cutover.md +403 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1a-graph-core.md +838 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1b-injected-edges.md +44 -0
- package/docs/required-peers.md +60 -0
- package/docs/specs/2026-06-08-wicked-loom-extraction.md +225 -0
- package/docs/specs/2026-06-10-codegraph-to-brain-migration.md +168 -0
- package/docs/v11/archetypes.md +231 -0
- package/hooks/hooks.json +275 -0
- package/hooks/scripts/bootstrap.py +1797 -0
- package/hooks/scripts/invoke.py +62 -0
- package/hooks/scripts/notification.py +166 -0
- package/hooks/scripts/permission_request.py +223 -0
- package/hooks/scripts/post_tool.py +1374 -0
- package/hooks/scripts/pre_compact.py +272 -0
- package/hooks/scripts/pre_tool.py +1278 -0
- package/hooks/scripts/prompt_submit.py +1109 -0
- package/hooks/scripts/session_end.py +95 -0
- package/hooks/scripts/stop.py +624 -0
- package/hooks/scripts/subagent_lifecycle.py +464 -0
- package/hooks/scripts/subscribers/on_gate_decided.py +84 -0
- package/hooks/scripts/task_completed.py +322 -0
- package/hooks/scripts/worktree_guard.py +148 -0
- package/hooks/subscriptions/on_gate_decided.json +9 -0
- package/install.mjs +128 -0
- package/package.json +41 -0
- package/pyproject.toml +59 -0
- package/schemas/README.md +32 -0
- package/schemas/assert_contracts.json +37 -0
- package/schemas/health_probe.json +46 -0
- package/schemas/plugin_status.json +37 -0
- package/scripts/_adapters/__init__.py +166 -0
- package/scripts/_adapters/jira_adapter.py +18 -0
- package/scripts/_adapters/linear_adapter.py +18 -0
- package/scripts/_adapters/miro_adapter.py +18 -0
- package/scripts/_adapters/notion_adapter.py +18 -0
- package/scripts/_agents.py +357 -0
- package/scripts/_brain_ingest/__init__.py +2 -0
- package/scripts/_brain_ingest/session_fact_extractor.py +341 -0
- package/scripts/_brain_port.py +163 -0
- package/scripts/_bus.py +1081 -0
- package/scripts/_bus_catalog_gen.py +93 -0
- package/scripts/_bus_consumers.json +46 -0
- package/scripts/_bus_emit.py +43 -0
- package/scripts/_capability_registry.py +334 -0
- package/scripts/_capability_resolver.py +322 -0
- package/scripts/_domain_store.py +559 -0
- package/scripts/_event_log_reader.py +154 -0
- package/scripts/_event_schema.py +305 -0
- package/scripts/_event_store.py +397 -0
- package/scripts/_heavy_cadence.py +324 -0
- package/scripts/_integration_resolver.py +321 -0
- package/scripts/_logger.py +151 -0
- package/scripts/_loom.py +482 -0
- package/scripts/_paths.py +133 -0
- package/scripts/_python.sh +22 -0
- package/scripts/_run.py +137 -0
- package/scripts/_session.py +502 -0
- package/scripts/_validate_registry.py +1042 -0
- package/scripts/_wicked_testing_probe.py +277 -0
- package/scripts/agentic/analyze_agents.py +391 -0
- package/scripts/agentic/detect_framework.py +420 -0
- package/scripts/agentic/issue_taxonomy.py +357 -0
- package/scripts/agentic/pattern_scorer.py +434 -0
- package/scripts/classify/persist.py +169 -0
- package/scripts/compiler/compile.py +552 -0
- package/scripts/compiler/phase0/VERDICT.md +60 -0
- package/scripts/compiler/phase0/detect.py +292 -0
- package/scripts/compiler/phase0/emit.py +228 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.bindings.json +30 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/command_iq.bindings.json +45 -0
- package/scripts/compiler/phase0/evidence/command_iq.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/memos.bindings.json +28 -0
- package/scripts/compiler/phase0/evidence/memos.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/verdict.json +104 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.bindings.json +29 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/wire-vault-transcript.txt +60 -0
- package/scripts/compiler/phase0/run_probe.py +234 -0
- package/scripts/compiler/phase0/wire_vault.py +310 -0
- package/scripts/crew/__init__.py +0 -0
- package/scripts/crew/_task_audit_writer.py +243 -0
- package/scripts/crew/_task_reader.py +389 -0
- package/scripts/crew/archetypes_v11.py +348 -0
- package/scripts/crew/detectors/__init__.py +12 -0
- package/scripts/crew/flow_compiler.py +121 -0
- package/scripts/crew/phase_manager.py +703 -0
- package/scripts/crew/scope_delta.py +264 -0
- package/scripts/crew/specialist_resolver.py +218 -0
- package/scripts/crew/stack_registry.py +189 -0
- package/scripts/data/data_profiler.py +271 -0
- package/scripts/data/ontology_recommender.py +231 -0
- package/scripts/data/schema_validator.py +267 -0
- package/scripts/delivery/drift.py +739 -0
- package/scripts/delivery/telemetry.py +614 -0
- package/scripts/engineering/patch/codegraph_db.py +125 -0
- package/scripts/engineering/patch/generators/__init__.py +56 -0
- package/scripts/engineering/patch/generators/base.py +373 -0
- package/scripts/engineering/patch/generators/csharp_generator.py +494 -0
- package/scripts/engineering/patch/generators/go_generator.py +430 -0
- package/scripts/engineering/patch/generators/java_generator.py +928 -0
- package/scripts/engineering/patch/generators/jsp_generator.py +454 -0
- package/scripts/engineering/patch/generators/kotlin_generator.py +561 -0
- package/scripts/engineering/patch/generators/perl_generator.py +510 -0
- package/scripts/engineering/patch/generators/php_generator.py +644 -0
- package/scripts/engineering/patch/generators/propagation_engine.py +929 -0
- package/scripts/engineering/patch/generators/python_generator.py +487 -0
- package/scripts/engineering/patch/generators/ruby_generator.py +463 -0
- package/scripts/engineering/patch/generators/rust_generator.py +400 -0
- package/scripts/engineering/patch/generators/sql_generator.py +594 -0
- package/scripts/engineering/patch/generators/typescript_generator.py +439 -0
- package/scripts/engineering/patch/patch.py +800 -0
- package/scripts/engineering/patch/safety.py +429 -0
- package/scripts/jam/__init__.py +0 -0
- package/scripts/jam/_bus_consumers.py +271 -0
- package/scripts/jam/agentic_cli_registry.py +703 -0
- package/scripts/jam/consensus.py +737 -0
- package/scripts/jam/detect_clis.py +409 -0
- package/scripts/jam/jam.py +283 -0
- package/scripts/jam/save_transcript.py +171 -0
- package/scripts/loom/__init__.py +22 -0
- package/scripts/loom/cli.py +170 -0
- package/scripts/loom/compose.py +180 -0
- package/scripts/loom/gate.py +120 -0
- package/scripts/loom/manifest.py +140 -0
- package/scripts/loom/resolve.py +59 -0
- package/scripts/persona/__init__.py +0 -0
- package/scripts/persona/registry.py +688 -0
- package/scripts/platform/guard_pipeline.py +1214 -0
- package/scripts/platform/guard_profiles.py +216 -0
- package/scripts/platform/observability/assert_contracts.py +434 -0
- package/scripts/platform/observability/health_probe.py +915 -0
- package/scripts/platform/observability/ops_log_viewer.py +209 -0
- package/scripts/platform/prereq_doctor.py +567 -0
- package/scripts/qe/conditions_manifest.py +260 -0
- package/scripts/qe/content_sanitizer.py +160 -0
- package/scripts/qe/coverage_tracker.py +535 -0
- package/scripts/qe/discover_scenarios.py +243 -0
- package/scripts/qe/evidence_tracker.py +232 -0
- package/scripts/qe/prove.py +196 -0
- package/scripts/qe/registry_lookup.py +80 -0
- package/scripts/qe/registry_store.py +176 -0
- package/scripts/qe/semantic_review.py +1005 -0
- package/scripts/qe/vault_gate.py +382 -0
- package/scripts/qe/verdict_audit.py +175 -0
- package/scripts/qe/verdict_schema.py +335 -0
- package/scripts/reset.py +220 -0
- package/scripts/resolve_path.py +22 -0
- package/scripts/sentinel/invariants.py +402 -0
- package/scripts/sentinel/pre_push.py +73 -0
- package/scripts/setup/__init__.py +1 -0
- package/scripts/setup/detect_state.py +114 -0
- package/scripts/setup/migrate_legacy.py +67 -0
- package/scripts/setup/migrate_v6_projects.py +232 -0
- package/scripts/setup/onboarding.py +114 -0
- package/scripts/smaht/_bus_consumers.py +98 -0
- package/scripts/smaht/adapters/__init__.py +83 -0
- package/scripts/smaht/adapters/brain_adapter.py +254 -0
- package/scripts/smaht/adapters/context7_adapter.py +438 -0
- package/scripts/smaht/adapters/delegation_adapter.py +104 -0
- package/scripts/smaht/adapters/domain_adapter.py +206 -0
- package/scripts/smaht/adapters/events_adapter.py +231 -0
- package/scripts/smaht/adapters/tools_adapter.py +49 -0
- package/scripts/smaht/cheatsheet_store.py +236 -0
- package/scripts/smaht/context_package.py +386 -0
- package/scripts/smaht/knowledge_graph.py +395 -0
- package/scripts/smaht/propose_skills.py +856 -0
- package/scripts/smaht/test_cheatsheet_store.py +428 -0
- package/scripts/statusline.py +151 -0
- package/scripts/where_am_i.py +236 -0
- package/skills/agentic/agentic-patterns/SKILL.md +91 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-design.md +283 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-operational.md +216 -0
- package/skills/agentic/agentic-patterns/refs/pattern-catalog.md +329 -0
- package/skills/agentic/context-engineering/SKILL.md +142 -0
- package/skills/agentic/context-engineering/refs/caching-and-optimization.md +251 -0
- package/skills/agentic/context-engineering/refs/compression-techniques.md +171 -0
- package/skills/agentic/context-engineering/refs/cost-calculation-budget.md +250 -0
- package/skills/agentic/context-engineering/refs/cost-optimization-reporting.md +300 -0
- package/skills/agentic/context-engineering/refs/selective-loading.md +179 -0
- package/skills/agentic/frameworks/SKILL.md +191 -0
- package/skills/agentic/frameworks/refs/framework-profiles-1.md +209 -0
- package/skills/agentic/frameworks/refs/framework-profiles-2.md +214 -0
- package/skills/agentic/frameworks/refs/migration-patterns-paths.md +267 -0
- package/skills/agentic/frameworks/refs/migration-patterns-testing.md +208 -0
- package/skills/agentic/refs/audit.md +134 -0
- package/skills/agentic/refs/design.md +137 -0
- package/skills/agentic/refs/frameworks.md +102 -0
- package/skills/agentic/refs/review.md +160 -0
- package/skills/agentic/review-methodology/SKILL.md +186 -0
- package/skills/agentic/review-methodology/refs/deliverable-templates.md +246 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-quality-testing.md +277 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-reliability-safety.md +253 -0
- package/skills/agentic/trust-and-safety/SKILL.md +177 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-actions.md +246 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-input-output.md +238 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-resources.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-advanced.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-core.md +215 -0
- package/skills/archetype/SKILL.md +104 -0
- package/skills/archetype/refs/build.md +142 -0
- package/skills/archetype/refs/decide.md +117 -0
- package/skills/archetype/refs/explore.md +66 -0
- package/skills/archetype/refs/incident.md +149 -0
- package/skills/archetype/refs/migrate.md +170 -0
- package/skills/archetype/refs/modernize.md +187 -0
- package/skills/archetype/refs/review.md +145 -0
- package/skills/archetype/refs/ship.md +117 -0
- package/skills/archetype/refs/specify.md +108 -0
- package/skills/archetype/refs/triage.md +62 -0
- package/skills/classify/SKILL.md +157 -0
- package/skills/data/analysis/SKILL.md +125 -0
- package/skills/data/analysis/refs/templates.md +170 -0
- package/skills/data/data/SKILL.md +118 -0
- package/skills/data/data/refs/examples.md +235 -0
- package/skills/data/ml/SKILL.md +125 -0
- package/skills/data/ml/refs/templates.md +172 -0
- package/skills/data/pipeline/SKILL.md +136 -0
- package/skills/data/pipeline/refs/templates.md +175 -0
- package/skills/data/refs/analyze.md +123 -0
- package/skills/data/refs/data.md +95 -0
- package/skills/data/refs/ml.md +118 -0
- package/skills/data/refs/pipeline.md +92 -0
- package/skills/deliberate/SKILL.md +125 -0
- package/skills/deliberate/refs/architecture-lens.md +37 -0
- package/skills/deliberate/refs/assumptions-lens.md +43 -0
- package/skills/deliberate/refs/code-lens.md +38 -0
- package/skills/deliberate/refs/content-lens.md +37 -0
- package/skills/deliberate/refs/data-lens.md +43 -0
- package/skills/deliberate/refs/design-lens.md +37 -0
- package/skills/deliberate/refs/opportunity-patterns.md +107 -0
- package/skills/deliberate/refs/ops-lens.md +43 -0
- package/skills/deliberate/refs/product-lens.md +46 -0
- package/skills/deliberate/refs/rethink-framework.md +77 -0
- package/skills/deliberate/refs/security-lens.md +47 -0
- package/skills/deliberate/refs/testing-lens.md +45 -0
- package/skills/deliberate/refs/timing-lens.md +42 -0
- package/skills/engineering/architecture/SKILL.md +177 -0
- package/skills/engineering/architecture/refs/adr-template.md +268 -0
- package/skills/engineering/architecture/refs/architecture-template-deployment.md +176 -0
- package/skills/engineering/architecture/refs/architecture-template-design.md +300 -0
- package/skills/engineering/architecture/refs/examples-ecommerce-analytics.md +299 -0
- package/skills/engineering/architecture/refs/examples-saas-trading.md +291 -0
- package/skills/engineering/architecture/refs/patterns-advanced.md +238 -0
- package/skills/engineering/architecture/refs/patterns-structural.md +303 -0
- package/skills/engineering/backend/SKILL.md +102 -0
- package/skills/engineering/backend/refs/checklists.md +146 -0
- package/skills/engineering/backend/refs/patterns.md +181 -0
- package/skills/engineering/debugging/SKILL.md +99 -0
- package/skills/engineering/debugging/refs/patterns.md +248 -0
- package/skills/engineering/debugging/refs/process.md +203 -0
- package/skills/engineering/docs/SKILL.md +97 -0
- package/skills/engineering/engineering/SKILL.md +88 -0
- package/skills/engineering/engineering/refs/checklists.md +91 -0
- package/skills/engineering/engineering/refs/templates.md +71 -0
- package/skills/engineering/frontend/SKILL.md +92 -0
- package/skills/engineering/frontend/refs/checklists.md +96 -0
- package/skills/engineering/frontend/refs/patterns.md +167 -0
- package/skills/engineering/integration/SKILL.md +183 -0
- package/skills/engineering/integration/refs/error-handling-best-practices.md +139 -0
- package/skills/engineering/integration/refs/error-handling-error-classes-typescript.md +212 -0
- package/skills/engineering/integration/refs/error-handling-error-response-format.md +301 -0
- package/skills/engineering/integration/refs/error-handling-retry-strategies.md +190 -0
- package/skills/engineering/integration/refs/event-schemas-best-practices.md +225 -0
- package/skills/engineering/integration/refs/event-schemas-event-naming-conventions.md +303 -0
- package/skills/engineering/integration/refs/event-schemas-event-structure.md +288 -0
- package/skills/engineering/integration/refs/graphql-best-practices.md +138 -0
- package/skills/engineering/integration/refs/graphql-error-handling.md +198 -0
- package/skills/engineering/integration/refs/graphql-graphql-basics.md +197 -0
- package/skills/engineering/integration/refs/graphql-query-examples.md +226 -0
- package/skills/engineering/integration/refs/graphql-schema-design-patterns.md +280 -0
- package/skills/engineering/integration/refs/grpc-grpc-overview.md +246 -0
- package/skills/engineering/integration/refs/grpc-implementation-examples.md +269 -0
- package/skills/engineering/integration/refs/grpc-metadata-headers.md +304 -0
- package/skills/engineering/integration/refs/grpc-testing.md +49 -0
- package/skills/engineering/integration/refs/openapi-template.yaml +682 -0
- package/skills/engineering/integration/refs/resilience-best-practices.md +109 -0
- package/skills/engineering/integration/refs/resilience-circuit-breaker-pattern.md +302 -0
- package/skills/engineering/integration/refs/resilience-rate-limiting.md +245 -0
- package/skills/engineering/integration/refs/resilience-timeout-pattern.md +212 -0
- package/skills/engineering/integration/refs/rest-api-fundamentals.md +299 -0
- package/skills/engineering/integration/refs/rest-api-responses-security.md +302 -0
- package/skills/engineering/integration/refs/versioning-breaking-changes.md +237 -0
- package/skills/engineering/integration/refs/versioning-migration-strategies.md +257 -0
- package/skills/engineering/integration/refs/versioning-versioning-strategies.md +296 -0
- package/skills/engineering/large-scale-migration/SKILL.md +65 -0
- package/skills/engineering/patch/SKILL.md +184 -0
- package/skills/engineering/refs/arch.md +126 -0
- package/skills/engineering/refs/debug.md +142 -0
- package/skills/engineering/refs/docs.md +190 -0
- package/skills/engineering/refs/plan.md +109 -0
- package/skills/engineering/refs/review.md +138 -0
- package/skills/engineering/system-design/SKILL.md +154 -0
- package/skills/engineering/system-design/refs/anti-patterns-big-ball-of-mud.md +254 -0
- package/skills/engineering/system-design/refs/anti-patterns-circular-dependencies.md +241 -0
- package/skills/engineering/system-design/refs/anti-patterns-feature-envy.md +168 -0
- package/skills/engineering/system-design/refs/anti-patterns-god-object.md +247 -0
- package/skills/engineering/system-design/refs/component-template-lifecycle.md +210 -0
- package/skills/engineering/system-design/refs/component-template-maintenance.md +219 -0
- package/skills/engineering/system-design/refs/component-template-structure.md +276 -0
- package/skills/engineering/system-design/refs/dependencies-best-practices.md +260 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-principles.md +295 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-tracking.md +261 -0
- package/skills/engineering/system-design/refs/interface-template-lifecycle.md +211 -0
- package/skills/engineering/system-design/refs/interface-template-maintenance.md +212 -0
- package/skills/engineering/system-design/refs/interface-template-structure.md +269 -0
- package/skills/engineering/system-design/refs/patterns-dependency-injection.md +151 -0
- package/skills/engineering/system-design/refs/patterns-facade-pattern.md +257 -0
- package/skills/engineering/system-design/refs/patterns-layered-pattern.md +204 -0
- package/skills/engineering/system-design/refs/patterns-plugin-architecture.md +229 -0
- package/skills/engineering/unit-test-quality/SKILL.md +194 -0
- package/skills/ground/SKILL.md +130 -0
- package/skills/integration-discovery/SKILL.md +168 -0
- package/skills/integration-discovery/refs/capabilities.md +282 -0
- package/skills/integration-discovery/refs/cli-detection.md +295 -0
- package/skills/integration-discovery/refs/discovery-sources.md +233 -0
- package/skills/integration-discovery/refs/task-patterns.md +206 -0
- package/skills/jam/SKILL.md +45 -0
- package/skills/jam/refs/facilitation-patterns.md +125 -0
- package/skills/jam/refs/quick.md +64 -0
- package/skills/jam/refs/synthesis-patterns.md +114 -0
- package/skills/multi-model/SKILL.md +168 -0
- package/skills/multi-model/refs/aichat.md +119 -0
- package/skills/multi-model/refs/aider.md +100 -0
- package/skills/multi-model/refs/auditability.md +238 -0
- package/skills/multi-model/refs/codex.md +169 -0
- package/skills/multi-model/refs/context.md +306 -0
- package/skills/multi-model/refs/copilot.md +161 -0
- package/skills/multi-model/refs/examples.md +224 -0
- package/skills/multi-model/refs/gemini.md +185 -0
- package/skills/multi-model/refs/goose.md +112 -0
- package/skills/multi-model/refs/llm.md +124 -0
- package/skills/multi-model/refs/opencode.md +196 -0
- package/skills/multi-model/refs/orchestration.md +188 -0
- package/skills/multi-model/refs/pi.md +208 -0
- package/skills/persona/SKILL.md +144 -0
- package/skills/persona/refs/define.md +133 -0
- package/skills/platform/actions/refs/actions.md +114 -0
- package/skills/platform/audit/SKILL.md +174 -0
- package/skills/platform/audit/refs/audit.md +145 -0
- package/skills/platform/audit/refs/checklists-evidence-operations.md +144 -0
- package/skills/platform/audit/refs/checklists-gdpr-pci-evidence.md +213 -0
- package/skills/platform/audit/refs/checklists-soc2-hipaa.md +120 -0
- package/skills/platform/audit/refs/frameworks-gdpr-pci.md +225 -0
- package/skills/platform/audit/refs/frameworks-soc2-hipaa.md +228 -0
- package/skills/platform/compliance/SKILL.md +169 -0
- package/skills/platform/compliance/refs/checklists.md +303 -0
- package/skills/platform/compliance/refs/compliance.md +117 -0
- package/skills/platform/compliance/refs/frameworks.md +282 -0
- package/skills/platform/errors/SKILL.md +183 -0
- package/skills/platform/errors/refs/patterns.md +271 -0
- package/skills/platform/errors/refs/severity.md +264 -0
- package/skills/platform/gate-benchmark-rebaseline/SKILL.md +108 -0
- package/skills/platform/gh/refs/gh.md +105 -0
- package/skills/platform/gh-cli/SKILL.md +120 -0
- package/skills/platform/gh-cli/refs/automation.md +63 -0
- package/skills/platform/gh-cli/refs/gh-ops.md +103 -0
- package/skills/platform/gh-cli/refs/patterns.md +132 -0
- package/skills/platform/gh-cli/scripts/gh_ops.py +454 -0
- package/skills/platform/github-actions/SKILL.md +159 -0
- package/skills/platform/github-actions/refs/security.md +150 -0
- package/skills/platform/github-actions/refs/templates.md +230 -0
- package/skills/platform/github-actions/refs/troubleshooting.md +174 -0
- package/skills/platform/github-actions/scripts/workflow_gen.py +215 -0
- package/skills/platform/gitlab-ci/SKILL.md +140 -0
- package/skills/platform/gitlab-ci/refs/templates.md +223 -0
- package/skills/platform/gitlab-ci/refs/troubleshooting.md +213 -0
- package/skills/platform/glab-cli/SKILL.md +133 -0
- package/skills/platform/glab-cli/refs/ci-debugging.md +95 -0
- package/skills/platform/glab-cli/refs/glab-ops.md +78 -0
- package/skills/platform/glab-cli/refs/patterns.md +131 -0
- package/skills/platform/glab-cli/scripts/glab_ops.py +313 -0
- package/skills/platform/health/SKILL.md +177 -0
- package/skills/platform/health/refs/health.md +72 -0
- package/skills/platform/health/refs/sources.md +302 -0
- package/skills/platform/incident/refs/incident.md +136 -0
- package/skills/platform/infra/refs/infra.md +104 -0
- package/skills/platform/observability/SKILL.md +128 -0
- package/skills/platform/observability/refs/toolchain-discovery.md +243 -0
- package/skills/platform/prereq-doctor/SKILL.md +57 -0
- package/skills/platform/prereq-doctor/refs/tool-registry.md +58 -0
- package/skills/platform/traces/refs/traces.md +101 -0
- package/skills/product/acceptance-criteria/SKILL.md +193 -0
- package/skills/product/acceptance-criteria/refs/ac-anti-patterns.md +291 -0
- package/skills/product/acceptance-criteria/refs/ac-examples.md +277 -0
- package/skills/product/accessibility/SKILL.md +197 -0
- package/skills/product/accessibility/refs/aria-patterns-basics.md +250 -0
- package/skills/product/accessibility/refs/aria-patterns-dynamic.md +187 -0
- package/skills/product/accessibility/refs/aria-patterns-interactive.md +246 -0
- package/skills/product/accessibility/refs/audit-report-findings.md +248 -0
- package/skills/product/accessibility/refs/audit-report-results.md +276 -0
- package/skills/product/accessibility/refs/common-violations-html-css.md +270 -0
- package/skills/product/accessibility/refs/common-violations-interactive.md +303 -0
- package/skills/product/accessibility/refs/keyboard-testing-basics.md +222 -0
- package/skills/product/accessibility/refs/keyboard-testing-patterns.md +173 -0
- package/skills/product/accessibility/refs/screen-reader-testing-advanced.md +294 -0
- package/skills/product/accessibility/refs/screen-reader-testing-commands.md +255 -0
- package/skills/product/accessibility/refs/wcag-checklist.md +262 -0
- package/skills/product/analyze/SKILL.md +164 -0
- package/skills/product/analyze/refs/algorithms.md +238 -0
- package/skills/product/analyze/refs/sentiment-patterns.md +180 -0
- package/skills/product/imagery/SKILL.md +97 -0
- package/skills/product/imagery/alter/SKILL.md +105 -0
- package/skills/product/imagery/alter/refs/editing-reference.md +146 -0
- package/skills/product/imagery/create/SKILL.md +109 -0
- package/skills/product/imagery/create/refs/prompt-engineering.md +112 -0
- package/skills/product/imagery/create/refs/provider-reference.md +219 -0
- package/skills/product/imagery/review/SKILL.md +101 -0
- package/skills/product/imagery/review/refs/analysis-lenses.md +136 -0
- package/skills/product/imagery/review/refs/quality-gates.md +153 -0
- package/skills/product/imagery/scripts/analyze.sh +23 -0
- package/skills/product/imagery/scripts/edit.sh +14 -0
- package/skills/product/imagery/scripts/generate.sh +13 -0
- package/skills/product/imagery/scripts/provider.py +787 -0
- package/skills/product/listen/SKILL.md +175 -0
- package/skills/product/listen/refs/channels.md +220 -0
- package/skills/product/mockup/SKILL.md +106 -0
- package/skills/product/mockup/refs/templates.md +95 -0
- package/skills/product/refs/a11y.md +109 -0
- package/skills/product/refs/acceptance.md +79 -0
- package/skills/product/refs/align.md +67 -0
- package/skills/product/refs/analyze.md +77 -0
- package/skills/product/refs/elicit.md +67 -0
- package/skills/product/refs/listen.md +58 -0
- package/skills/product/refs/mockup.md +77 -0
- package/skills/product/refs/screenshot.md +59 -0
- package/skills/product/refs/strategy.md +77 -0
- package/skills/product/refs/synthesize.md +54 -0
- package/skills/product/refs/ux-review.md +68 -0
- package/skills/product/refs/ux.md +101 -0
- package/skills/product/requirements-analysis/SKILL.md +136 -0
- package/skills/product/requirements-analysis/refs/requirements-example-auth.md +264 -0
- package/skills/product/requirements-analysis/refs/requirements-example-export-integration.md +88 -0
- package/skills/product/requirements-analysis/refs/requirements-output-format-template.md +247 -0
- package/skills/product/requirements-analysis/refs/user-story-guide.md +296 -0
- package/skills/product/requirements-graph/SKILL.md +155 -0
- package/skills/product/requirements-graph/refs/examples.md +223 -0
- package/skills/product/requirements-graph/refs/schema.md +162 -0
- package/skills/product/requirements-migrate/SKILL.md +90 -0
- package/skills/product/requirements-navigate/SKILL.md +141 -0
- package/skills/product/requirements-navigate/refs/meta-templates.md +141 -0
- package/skills/product/screenshot/SKILL.md +147 -0
- package/skills/product/strategy/SKILL.md +198 -0
- package/skills/product/strategy/refs/frameworks.md +295 -0
- package/skills/product/strategy/refs/templates-competitive-market.md +299 -0
- package/skills/product/strategy/refs/templates-roi-value.md +207 -0
- package/skills/product/synthesize/SKILL.md +116 -0
- package/skills/product/synthesize/refs/journey-mapping.md +212 -0
- package/skills/product/synthesize/refs/prioritization.md +193 -0
- package/skills/product/ux-review/SKILL.md +164 -0
- package/skills/product/visual-direction/SKILL.md +137 -0
- package/skills/product/visual-review/SKILL.md +131 -0
- package/skills/runtime-exec/SKILL.md +138 -0
- package/skills/search/codebase-narrator/SKILL.md +131 -0
- package/skills/search/codebase-narrator/refs/output-template.md +100 -0
- package/skills/smaht/SKILL.md +72 -0
- package/skills/smaht/discovery/SKILL.md +65 -0
- package/skills/smaht/intent/SKILL.md +89 -0
- package/skills/smaht/propose-skills/SKILL.md +142 -0
- package/skills/smaht/refs/state.md +76 -0
- package/skills/swarm/SKILL.md +132 -0
- package/skills/swarm/refs/fan-out.md +104 -0
- package/skills/swarm/refs/independent-verification.md +86 -0
- package/skills/swarm/refs/receipts-and-evidence.md +69 -0
- package/skills/swarm/refs/recon-synthesis.md +87 -0
- package/skills/swarm/refs/ship-discipline.md +90 -0
- package/skills/wickedizer/SKILL.md +147 -0
- package/skills/wickedizer/refs/artifact-types.md +238 -0
- package/skills/wickedizer/refs/complexity.md +278 -0
- package/skills/wickedizer/refs/de-ai-pass.md +140 -0
- package/skills/wickedizer/refs/failure-modes.md +249 -0
- package/skills/wickedizer/refs/patterns.md +282 -0
- package/skills/wickedizer/refs/review.md +221 -0
- package/skills/wickedizer/refs/structure.md +282 -0
- package/skills/wickedizer/refs/voice-profile.md +125 -0
- package/skills/workflow/SKILL.md +132 -0
- package/skills/workflow/refs/archetype-adjustments.md +205 -0
- package/skills/workflow/refs/evidence.md +58 -0
- package/skills/workflow/refs/integration.md +130 -0
- package/skills/workflow/refs/risk-dimension-signals.md +260 -0
- package/skills/workflow/refs/scoring-rubric.md +49 -0
- package/skills/workflow/refs/signal-keywords.md +176 -0
- package/skills/workflow/refs/specialist-routing-rules.md +159 -0
- package/skills/worktrees/SKILL.md +87 -0
- package/skills/worktrees/refs/recipes.md +96 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
# wicked-loom — Contract Phase (garden-side strangler migration) — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Complete the strangler. The **cutover** (`docs/plans/2026-06-08-wicked-loom-cutover.md`) gave garden's `resolve` and `gate` surfaces a *loom-first head* that **falls back to an unchanged in-process body** when loom is unresolvable or errors. That fallback was the cutover's whole safety net — rollback was `WICKED_LOOM_CUTOVER=off`. The cutover is now proven; this **contract** phase deletes the now-redundant in-process re-derivation body so **loom is authoritative** for both shimmed surfaces. The in-process re-derivation code (the `_run` + JSON-parse body of `cross_check`; the `allow_npx=True` resolution ladder fallback in `resolve_vault`) is **removed**, not flag-flipped.
|
|
6
|
+
|
|
7
|
+
**Critical invariant (the whole point of the gate):** honest evidence is preserved verbatim. Removing the in-process fallback must NEVER open a vacuous-pass path. With loom unresolvable / erroring, the gate MUST fail **closed** (`available: False`, `gate: "unavailable"`, `satisfied: false`) — exactly as the in-process body did when the vault was unresolvable. We are deleting a *redundant re-derivation engine*, not a *fail-closed guard*. I2 (fail-closed) holds unchanged.
|
|
8
|
+
|
|
9
|
+
**Scope — the SHIMMED surfaces ONLY (`resolve` + `gate`):**
|
|
10
|
+
- **`cross_check` (gate):** remove the in-process `_run(...)` + JSON-parse re-derivation body that the loom head currently falls through to. loom `gate` becomes the sole re-derivation path. On any loom error/unresolvable → fail-closed `available: False` / `overall: "ERROR"`.
|
|
11
|
+
- **`resolve_vault` (resolve):** make loom authoritative for the `allow_npx=True` (run-the-vault) path — remove the redundant in-process npx-bearing ladder fallback. **KEEP** the in-process ladder for the `allow_npx=False` path because `vault_available()` calls `resolve_vault(allow_npx=False)` to detect a **concrete** install — loom's `resolve` would report the npx last-resort as resolvable and corrupt that "installed" signal (the cutover's `allow_npx` boundary, plan line 533). So `vault_available()` stays in-process and unbroken.
|
|
12
|
+
- **The flag `WICKED_LOOM_CUTOVER`:** kept as a documented **emergency disable**, not removed. There is no longer an in-process gate to "fall to", so `off`/`auto`-unresolvable now mean *the loom path is not taken → the surface is unavailable → the gate fails closed*. `off` is therefore a coarse "stop using loom" kill that **fails closed** (never a vacuous pass), parallel to `WICKED_VAULT_BIN=""`. Decision + justification below.
|
|
13
|
+
|
|
14
|
+
**Out of scope (DO NOT TOUCH — future increments, NOT shimmed by the cutover):**
|
|
15
|
+
- The **flow surface** (`phase_manager.approve_phase`'s loom *parity mirror* + `flow_compiler.py`). The cutover made this a **read-only parity mirror** — it never *replaced* an in-process path, it only cross-checks the park-at-hard-gate decision and emits a `wicked.loom.parity_mismatch` event. There is no in-process re-derivation body to delete here. `phase_manager`'s DomainStore `ProjectState` storage is the STAY surface (spec §6). LEAVE the mirror, `flow_compiler`, and all flow contract tests intact.
|
|
16
|
+
- The broader spec §6 inventory the cutover did NOT shim: `phase_manager` storage move to loom, deletion of `_integration_resolver.py` / `_capability_resolver.py` / `_capability_registry.py`, the `bootstrap.py` peer-detect → loom compose split, the `_bus.py` consumer/projector split, the `_event_*` trio. **All future increments. LEAVE ALONE.**
|
|
17
|
+
- The **compiler** (`scripts/compiler/**`) — self-contained, resolves the vault directly via npx, imports nothing from the garden (AST-enforced). Untouched.
|
|
18
|
+
- The **bootstrap loom peer-probe** (`_check_loom_dependency`) — its detection logic stays (compose is a future increment). Only its prose claim "garden falls back to its in-process runtime when loom is absent" is corrected (no longer true post-contract).
|
|
19
|
+
|
|
20
|
+
**Tech Stack:** Python 3.9+ (stdlib only — garden's hook discipline; loom is reached as a subprocess, never imported). `pytest` (`testpaths = ["tests"]`; `tests/conftest.py` puts `scripts/` on `sys.path[0]`). Tests inject the loom subprocess runner (mock `_loom._default_run` / patch `_loom.resolve_loom`) so **no test spawns a real loom or touches the network** — CI stays Python-only.
|
|
21
|
+
|
|
22
|
+
**Bulletproof standards:** R1–R6 (no dead code — the removed in-process body is dead once loom is authoritative; no swallowed errors — loom errors surface as fail-closed verdicts, never silent pass). T1–T6 (determinism, isolation, single-assertion focus, descriptive names, provenance). The fail-closed invariant gets the strongest coverage: a test proving loom-unresolvable → `unavailable`, never a pass.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## File Structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
wicked-garden/
|
|
30
|
+
├── scripts/
|
|
31
|
+
│ ├── _loom.py # MODIFY — docstrings only: use_loom()/cutover_mode()
|
|
32
|
+
│ │ # no longer describe an "in-process fallback"; off now
|
|
33
|
+
│ │ # = loom disabled = fail-closed for shimmed surfaces.
|
|
34
|
+
│ │ # (Logic unchanged — the flag semantics are unchanged;
|
|
35
|
+
│ │ # only the CALLERS stop having an in-process body.)
|
|
36
|
+
│ └── qe/
|
|
37
|
+
│ └── vault_gate.py # MODIFY — DELETE the in-process re-derivation body of
|
|
38
|
+
│ # cross_check (the loom head becomes the whole function);
|
|
39
|
+
│ # DELETE the allow_npx=True in-process ladder fallback in
|
|
40
|
+
│ # resolve_vault (loom authoritative), KEEP the allow_npx=False
|
|
41
|
+
│ # ladder for vault_available(). _run() is deleted (dead).
|
|
42
|
+
├── hooks/scripts/
|
|
43
|
+
│ └── bootstrap.py # MODIFY — prose only: _check_loom_dependency message no longer
|
|
44
|
+
│ # claims an in-process fallback (loom is now load-bearing).
|
|
45
|
+
├── docs/
|
|
46
|
+
│ └── required-peers.md # MODIFY — loom row + load-bearing note: loom is the runtime,
|
|
47
|
+
│ # not a shim; the in-process fallback is gone post-contract.
|
|
48
|
+
└── tests/
|
|
49
|
+
├── conftest.py # MODIFY — drop the WICKED_LOOM_CUTOVER=off autouse default for
|
|
50
|
+
│ # the gate/resolve surfaces; tests now exercise the loom path
|
|
51
|
+
│ # via injected/mocked runners. (Keep path ordering + bus reset.)
|
|
52
|
+
├── qe/
|
|
53
|
+
│ ├── test_loom_gate_contract.py # REPLACE — parity (loom==in-process) tests are MOOT
|
|
54
|
+
│ │ # (no in-process to compare). Rewrite as loom-AUTHORITATIVE:
|
|
55
|
+
│ │ # loom is the only re-derivation path; fail-closed when
|
|
56
|
+
│ │ # loom absent/errors. KEEP a strong fail-closed test.
|
|
57
|
+
│ └── test_vault_gate.py # MODIFY — the in-process-path unit tests that relied on
|
|
58
|
+
│ # mocking vg._run / the in-process cross_check body are
|
|
59
|
+
│ # reframed to drive the loom path (mock the loom runner).
|
|
60
|
+
│ # Keep the real-vault VaultBackedGateTests (skip-guarded).
|
|
61
|
+
└── crew/
|
|
62
|
+
└── test_loom_resolve_contract.py # REPLACE — parity tests are moot. Rewrite as loom-
|
|
63
|
+
# authoritative resolve: loom is the only resolve path
|
|
64
|
+
# for allow_npx=True; loom-unresolvable → None (no
|
|
65
|
+
# in-process npx fallback). vault_available() (allow_npx
|
|
66
|
+
# =False) still resolves in-process — assert that.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Responsibilities after the contract:**
|
|
70
|
+
- `resolve_vault(allow_npx=True)` = *ask loom where the vault is.* loom authoritative; loom-unresolvable → None (fail-closed). No in-process npx ladder on this path.
|
|
71
|
+
- `resolve_vault(allow_npx=False)` = *is a concrete vault install present?* in-process ladder (env → config → PATH → node_modules; **no npx, no loom**). This is `vault_available()`'s probe — UNCHANGED.
|
|
72
|
+
- `cross_check` = *re-derive the verdict via loom `gate`.* loom is the sole re-derivation engine; any loom error → `available: False` (fail-closed). No in-process `_run`/parse body.
|
|
73
|
+
- `gate_satisfied` = unchanged structure: probe `resolve_vault(project_dir)` (now loom-routed) → `cross_check` → fail-closed when unavailable. Inherits the loom path transparently.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Flag decision (justified, per the prompt)
|
|
78
|
+
|
|
79
|
+
**KEEP `WICKED_LOOM_CUTOVER`; redefine `off` as a documented emergency disable that FAILS CLOSED.** Rationale:
|
|
80
|
+
|
|
81
|
+
- The flag's *logic* (`_loom.use_loom`: `off`→False, `on`→True, `auto`→resolvable) is unchanged. What changes is what "loom not used" *means* to the callers. Pre-contract: fall to the in-process body. Post-contract: there is no in-process body — so "loom not used" means the shimmed surface is **unavailable**, and the gate **fails closed** (I2). That is the only honest meaning left.
|
|
82
|
+
- Removing the flag entirely would discard a coarse operational kill-switch with zero cost to keep. Keeping it gives operators a one-env-var way to stop garden from shelling out to loom during an incident (e.g. a wedged `npx`), accepting that gating then fails closed until loom is restored. That matches the vault's own `WICKED_VAULT_BIN=""` kill-switch posture: disable cleanly, fail closed, never thrash, never vacuous-pass.
|
|
83
|
+
- We document the new semantics in `_loom.py` docstrings + `required-peers.md` so no one reads `off` as "safe in-process mode" (it no longer exists). The bootstrap probe already treats `off`/`WICKED_LOOM_BIN` set as "operator is driving deliberately, don't nag" — that stays correct.
|
|
84
|
+
|
|
85
|
+
Net: loom is now a **load-bearing required peer for gating**. `off` is an emergency disable, not a fallback selector.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Task 1: Contract the `gate` surface — `cross_check` loom-authoritative, in-process body deleted
|
|
90
|
+
|
|
91
|
+
**Files:**
|
|
92
|
+
- Modify: `~/Projects/wicked-garden/scripts/qe/vault_gate.py` (`cross_check`; delete in-process body + `_run`)
|
|
93
|
+
- Replace: `~/Projects/wicked-garden/tests/qe/test_loom_gate_contract.py`
|
|
94
|
+
- Modify: `~/Projects/wicked-garden/tests/qe/test_vault_gate.py`
|
|
95
|
+
|
|
96
|
+
The cutover's `cross_check` is: loom head → (on success) return mapped verdict; (on loom error) **fall through** to `args = [...]; run = _run(args, ...); parse`. The contract deletes everything from `args = ["cross-check", ...]` onward, plus the now-unused `_run` helper. The loom head's *trailing comment* ("loom errored -> fall through to in-process re-derivation") changes to a **fail-closed return**.
|
|
97
|
+
|
|
98
|
+
- [ ] **Step 1: Rewrite the gate contract test as loom-authoritative (TDD — write the new spec first)**
|
|
99
|
+
|
|
100
|
+
Replace `tests/qe/test_loom_gate_contract.py` entirely. The parity tests (loom==in-process) are moot — there is no in-process path. The new tests assert: loom is the ONLY re-derivation path; fail-closed when loom errors/unresolvable; `--with-attestations` still forwarded; the `gate: "unavailable"` mapping still fails closed. Hermetic — mock `_loom._default_run` / patch `_loom.resolve_loom`, never real npx.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import os
|
|
104
|
+
import sys
|
|
105
|
+
import unittest
|
|
106
|
+
from pathlib import Path
|
|
107
|
+
from unittest.mock import patch
|
|
108
|
+
|
|
109
|
+
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
110
|
+
for _p in (_REPO_ROOT / "scripts", _REPO_ROOT / "scripts" / "qe"):
|
|
111
|
+
if str(_p) not in sys.path:
|
|
112
|
+
sys.path.insert(0, str(_p))
|
|
113
|
+
|
|
114
|
+
import _loom # noqa: E402
|
|
115
|
+
import vault_gate as vg # noqa: E402
|
|
116
|
+
|
|
117
|
+
_PROJECT = Path("/tmp/proj-loom-gate")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class GateLoomAuthoritative(unittest.TestCase):
|
|
121
|
+
"""Contract phase: loom `gate` is the SOLE re-derivation path. The
|
|
122
|
+
in-process cross_check body is gone. loom unresolvable/errors → the gate
|
|
123
|
+
is unavailable and FAILS CLOSED — never a vacuous pass (I2)."""
|
|
124
|
+
|
|
125
|
+
def setUp(self):
|
|
126
|
+
# Default the suite to off; each test opts the loom path on explicitly.
|
|
127
|
+
for v in ("WICKED_VAULT_BIN", "WICKED_LOOM_BIN"):
|
|
128
|
+
os.environ.pop(v, None)
|
|
129
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "on"
|
|
130
|
+
|
|
131
|
+
def tearDown(self):
|
|
132
|
+
for v in ("WICKED_VAULT_BIN", "WICKED_LOOM_BIN", "WICKED_LOOM_CUTOVER"):
|
|
133
|
+
os.environ.pop(v, None)
|
|
134
|
+
|
|
135
|
+
def _loom_gate_runner(self, overall):
|
|
136
|
+
def fake_run(prefix, args, timeout):
|
|
137
|
+
import json
|
|
138
|
+
verdict = {"satisfied": overall == "PASS", "overall": overall,
|
|
139
|
+
"gate": "vault-cross-check", "claims": [{"id": "tests-pass"}]}
|
|
140
|
+
return {"exit_code": 0 if overall == "PASS" else 1,
|
|
141
|
+
"stdout": json.dumps({"gate": verdict}), "stderr": "", "error": None}
|
|
142
|
+
return fake_run
|
|
143
|
+
|
|
144
|
+
def test_loom_pass_is_the_only_path(self):
|
|
145
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
146
|
+
patch.object(_loom, "_default_run", self._loom_gate_runner("PASS")):
|
|
147
|
+
cc = vg.cross_check("build-1", "test", project_dir=_PROJECT)
|
|
148
|
+
self.assertTrue(cc["available"])
|
|
149
|
+
self.assertEqual(cc["overall"], "PASS")
|
|
150
|
+
self.assertEqual(cc["claims"], [{"id": "tests-pass"}])
|
|
151
|
+
|
|
152
|
+
def test_loom_reject_surfaced(self):
|
|
153
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
154
|
+
patch.object(_loom, "_default_run", self._loom_gate_runner("REJECT")):
|
|
155
|
+
cc = vg.cross_check("build-1", "test", project_dir=_PROJECT)
|
|
156
|
+
self.assertTrue(cc["available"])
|
|
157
|
+
self.assertEqual(cc["overall"], "REJECT")
|
|
158
|
+
|
|
159
|
+
def test_loom_gate_unavailable_maps_to_fail_closed(self):
|
|
160
|
+
# loom reached, but vault unresolvable behind it -> gate: unavailable.
|
|
161
|
+
def fake_run(prefix, args, timeout):
|
|
162
|
+
import json
|
|
163
|
+
return {"exit_code": 1, "stdout": json.dumps(
|
|
164
|
+
{"gate": {"gate": "unavailable", "error": "no vault"}}),
|
|
165
|
+
"stderr": "", "error": None}
|
|
166
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
167
|
+
patch.object(_loom, "_default_run", fake_run):
|
|
168
|
+
cc = vg.cross_check("build-1", "test", project_dir=_PROJECT)
|
|
169
|
+
self.assertFalse(cc["available"])
|
|
170
|
+
self.assertEqual(cc["overall"], "ERROR")
|
|
171
|
+
|
|
172
|
+
def test_loom_error_fails_closed_no_in_process_pass(self):
|
|
173
|
+
# loom resolves but the subprocess errors (timeout/not-found). There is
|
|
174
|
+
# NO in-process fallback now -> cross_check must report unavailable.
|
|
175
|
+
def boom(prefix, args, timeout):
|
|
176
|
+
return {"exit_code": None, "stdout": "", "stderr": "",
|
|
177
|
+
"error": "loom call exceeded 120s"}
|
|
178
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
179
|
+
patch.object(_loom, "_default_run", boom):
|
|
180
|
+
cc = vg.cross_check("build-1", "test", project_dir=_PROJECT)
|
|
181
|
+
self.assertFalse(cc["available"])
|
|
182
|
+
self.assertEqual(cc["overall"], "ERROR")
|
|
183
|
+
|
|
184
|
+
def test_loom_unresolvable_fails_closed(self):
|
|
185
|
+
# auto + loom unresolvable -> the loom path is not taken; with no
|
|
186
|
+
# in-process body, cross_check reports unavailable (fail-closed).
|
|
187
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "auto"
|
|
188
|
+
with patch.object(_loom, "resolve_loom", return_value=None):
|
|
189
|
+
cc = vg.cross_check("build-1", "test", project_dir=_PROJECT)
|
|
190
|
+
self.assertFalse(cc["available"])
|
|
191
|
+
self.assertEqual(cc["overall"], "ERROR")
|
|
192
|
+
|
|
193
|
+
def test_gate_satisfied_fails_closed_when_loom_absent(self):
|
|
194
|
+
# The load-bearing fail-closed invariant end-to-end: loom unresolvable
|
|
195
|
+
# AND vault unresolvable -> gate_satisfied is unavailable, NOT a pass.
|
|
196
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "auto"
|
|
197
|
+
os.environ["WICKED_VAULT_BIN"] = "" # vault kill-switch too
|
|
198
|
+
with patch.object(_loom, "resolve_loom", return_value=None):
|
|
199
|
+
verdict = vg.gate_satisfied(_PROJECT, "build-1", "test")
|
|
200
|
+
self.assertFalse(verdict["satisfied"])
|
|
201
|
+
self.assertEqual(verdict["gate"], "unavailable")
|
|
202
|
+
self.assertFalse(verdict["re_derived"])
|
|
203
|
+
|
|
204
|
+
def test_with_attestations_forwarded_to_loom(self):
|
|
205
|
+
seen = {}
|
|
206
|
+
|
|
207
|
+
def fake_run(prefix, args, timeout):
|
|
208
|
+
import json
|
|
209
|
+
seen["args"] = args
|
|
210
|
+
return {"exit_code": 0, "stdout": json.dumps(
|
|
211
|
+
{"gate": {"satisfied": True, "overall": "PASS"}}), "stderr": "", "error": None}
|
|
212
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
213
|
+
patch.object(_loom, "_default_run", fake_run):
|
|
214
|
+
vg.cross_check("build-1", "review", project_dir=_PROJECT, with_attestations=True)
|
|
215
|
+
self.assertIn("--with-attestations", seen["args"])
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if __name__ == "__main__":
|
|
219
|
+
unittest.main()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- [ ] **Step 2: Run the test — confirm it fails on the right tests**
|
|
223
|
+
|
|
224
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/qe/test_loom_gate_contract.py -v`
|
|
225
|
+
Expected: `test_loom_unresolvable_fails_closed` and `test_loom_error_fails_closed_no_in_process_pass` FAIL (the current code, on loom-unresolvable/error, falls through to the in-process `_run` body, which — with a mocked-away vault or real npx — does NOT return the fail-closed `available: False` these tests demand). The others may pass against the current loom head. (Note: this test sets `WICKED_LOOM_CUTOVER=on` in setUp, overriding the autouse `off` default that we remove in Task 4.)
|
|
226
|
+
|
|
227
|
+
- [ ] **Step 3: Edit `vault_gate.py` — delete the in-process re-derivation body of `cross_check` and the `_run` helper**
|
|
228
|
+
|
|
229
|
+
In `cross_check`, the loom head currently ends with `# loom errored -> fall through to in-process re-derivation (fail-soft).` and then the function continues with the in-process body. Replace the END of the loom head and DELETE the in-process body. The new `cross_check` body is **only** the loom head, with the trailing fall-through turned into a fail-closed return:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
def cross_check(
|
|
233
|
+
scope: str,
|
|
234
|
+
phase: str,
|
|
235
|
+
*,
|
|
236
|
+
project_dir: Optional[Path] = None,
|
|
237
|
+
with_attestations: bool = False,
|
|
238
|
+
timeout: int = _DEFAULT_TIMEOUT,
|
|
239
|
+
) -> Dict[str, Any]:
|
|
240
|
+
"""Run the produces re-derivation via ``wicked-loom gate`` and return its
|
|
241
|
+
mechanical verdict.
|
|
242
|
+
|
|
243
|
+
loom is the **sole** re-derivation engine (the in-process vault path was
|
|
244
|
+
removed in the contract phase). loom re-hashes the recorded evidence and
|
|
245
|
+
re-runs the verifier, mapping the result onto ``{available, overall,
|
|
246
|
+
claims, ...}``.
|
|
247
|
+
|
|
248
|
+
Fail-closed (I2): when loom is unresolvable, errors, or reports the vault
|
|
249
|
+
unavailable behind it, ``available`` is False and ``overall`` is ERROR —
|
|
250
|
+
we never invent a PASS. There is no in-process fallback; a missing loom is
|
|
251
|
+
a missing gate, and a missing gate fails closed.
|
|
252
|
+
"""
|
|
253
|
+
if _loom is None or not _loom.use_loom(project_dir=project_dir):
|
|
254
|
+
# loom shim absent, or WICKED_LOOM_CUTOVER=off / auto-unresolvable.
|
|
255
|
+
# No in-process re-derivation remains -> the gate is unavailable.
|
|
256
|
+
return {"available": False, "overall": "ERROR",
|
|
257
|
+
"error": "wicked-loom not in use (disabled or unresolvable)",
|
|
258
|
+
"exit_code": None}
|
|
259
|
+
|
|
260
|
+
loom_args = ["gate", phase, "--scope", scope]
|
|
261
|
+
if with_attestations:
|
|
262
|
+
loom_args.append("--with-attestations")
|
|
263
|
+
out = _loom.run_json(loom_args, project_dir=project_dir, timeout=timeout)
|
|
264
|
+
if out["error"] is not None or not isinstance(out.get("json"), dict):
|
|
265
|
+
# loom unresolvable / timed out / non-JSON. Fail closed (I2).
|
|
266
|
+
return {"available": False, "overall": "ERROR",
|
|
267
|
+
"error": out.get("error") or "loom returned no usable verdict",
|
|
268
|
+
"exit_code": out.get("exit_code")}
|
|
269
|
+
|
|
270
|
+
verdict = out["json"].get("gate") or {}
|
|
271
|
+
if verdict.get("gate") == "unavailable":
|
|
272
|
+
# loom reached but the vault is unresolvable behind it -> fail closed.
|
|
273
|
+
return {"available": False, "overall": "ERROR",
|
|
274
|
+
"error": verdict.get("error", "vault unavailable via loom"),
|
|
275
|
+
"exit_code": out.get("exit_code")}
|
|
276
|
+
return {
|
|
277
|
+
"available": True,
|
|
278
|
+
"overall": verdict.get("overall", "ERROR"),
|
|
279
|
+
"exit_code": out.get("exit_code"),
|
|
280
|
+
"claims": verdict.get("claims", []),
|
|
281
|
+
"mode": verdict.get("mode"),
|
|
282
|
+
"contract_version": verdict.get("contract_version"),
|
|
283
|
+
"detail": verdict.get("detail"),
|
|
284
|
+
"raw": verdict,
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Then **delete the `_run` helper entirely** (the `def _run(...)` block under the `# Invocation` header) — it is now dead (R1: no dead code). Its sole caller was the deleted in-process `cross_check` body.
|
|
289
|
+
|
|
290
|
+
> Implementer note: keep the `_DEFAULT_TIMEOUT` constant (still used by the signature default). `subprocess` / `_argv_for` imports in `vault_gate.py` are still used by the `allow_npx=False` resolution ladder in `resolve_vault` (Task 2) — do NOT remove them. Confirm with a post-edit grep that `_run` has zero references.
|
|
291
|
+
|
|
292
|
+
- [ ] **Step 4: Reframe the in-process unit tests in `test_vault_gate.py`**
|
|
293
|
+
|
|
294
|
+
The real-vault `VaultBackedGateTests` (skip-guarded; resolves `WICKED_VAULT_BIN` / sibling checkout) drives `gate_satisfied` end-to-end. Post-contract, `gate_satisfied` → `cross_check` → loom (not the in-process `_run`). Those tests set `WICKED_VAULT_BIN` but do **not** set up loom — so with the autouse `off` default removed (Task 4) and `auto` resolving loom via npx on a dev box, they would try to shell a real loom. Guard them: skip unless a real loom is *also* resolvable, OR (simpler + hermetic) keep them pointed at the in-process vault by setting `WICKED_LOOM_CUTOVER=off` AND asserting the new fail-closed semantics. **Chosen approach:** these tests assert the *vault re-derivation contract*, which now lives in loom — so move that assertion to loom-backed coverage is out of scope (loom has its own suite). Here, mark `VaultBackedGateTests` to **also require a resolvable loom** and drive through it, OR skip when loom is absent. Concretely:
|
|
295
|
+
|
|
296
|
+
- In `VaultBackedGateTests.setUp`, after setting `WICKED_VAULT_BIN`, also require loom: add `WICKED_LOOM_CUTOVER` handling. Simplest hermetic fix that keeps the suite green on a Python-only CI: change the class skip guard to also skip when `_loom.resolve_loom(allow_npx=False)` is None (no concrete loom), since the gate now runs through loom. Update the skip reason to mention loom.
|
|
297
|
+
- The `RequiredFailClosedTests` (kill-switch `WICKED_VAULT_BIN=""`) currently assert `gate_satisfied` fails closed. With loom disabled too, they still must fail closed. Set `WICKED_LOOM_CUTOVER=off` in their `setUp` (alongside the vault kill-switch) so the gate is unambiguously unavailable; assert `gate: "unavailable"` unchanged. This keeps the anti-vacuous-PASS guarantee covered purely in-process (no loom, no vault → fail closed).
|
|
298
|
+
- The `ResolutionTests` (`resolve_vault` env cases) call `resolve_vault()` (allow_npx default True). Post-contract that is the loom-authoritative path. Set `WICKED_LOOM_CUTOVER=off` in their `setUp` so they exercise... no — `off` now makes `resolve_vault(allow_npx=True)` return None (loom disabled, no in-process fallback). These env-override tests (`WICKED_VAULT_BIN=/path`) are really testing the in-process ladder, which now only runs under `allow_npx=False`. **Reframe them to call `resolve_vault(allow_npx=False)`** (the concrete-install probe that keeps the in-process ladder). The `WICKED_VAULT_BIN` env override and `.mjs`→node and kill-switch cases all live on that path. Assert via `allow_npx=False`.
|
|
299
|
+
|
|
300
|
+
Apply these edits to `test_vault_gate.py`:
|
|
301
|
+
1. `ResolutionTests`: change each `vg.resolve_vault()` → `vg.resolve_vault(allow_npx=False)` (these assert the in-process concrete-install ladder, which is what `vault_available` uses and what survives the contract). The empty-env kill-switch test already calls `vault_available()` (allow_npx=False) — keep it.
|
|
302
|
+
2. `RequiredFailClosedTests.setUp`: add `os.environ["WICKED_LOOM_CUTOVER"] = "off"`; `tearDown` pops it. (Vault kill-switch + loom off → unambiguous fail-closed; the assertions are unchanged.)
|
|
303
|
+
3. `VaultBackedGateTests`: change the class decorator skip to also require loom — replace `@unittest.skipIf(_locate_vault() is None, ...)` with a guard that skips when either the vault OR a concrete loom is unavailable, and set `WICKED_LOOM_CUTOVER` appropriately in setUp so the gate routes through the real loom. If wiring a real loom into this test proves heavy, the pragmatic alternative (allowed): keep `VaultBackedGateTests` skip-guarded on the vault as today but set `WICKED_LOOM_CUTOVER=off` in setUp and convert its three assertions to the fail-closed shape (since with loom off the gate is unavailable) — i.e. these become "vault present but loom off → fail-closed" coverage. **Pick whichever keeps CI green and hermetic; document the choice in the test docstring.**
|
|
304
|
+
|
|
305
|
+
> Implementer note: the goal is zero failures on a Python-only CI (no node, no npx, no loom). The cleanest path: drive the loom surface with MOCKED runners in `test_loom_gate_contract.py` (Step 1), and in `test_vault_gate.py` keep the in-process *resolution-ladder* tests (via `allow_npx=False`) plus the fail-closed tests (loom off + vault off). The real-subprocess `VaultBackedGateTests` stays skip-guarded so it never runs in plain CI.
|
|
306
|
+
|
|
307
|
+
- [ ] **Step 5: Run the gate tests**
|
|
308
|
+
|
|
309
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/qe/test_loom_gate_contract.py tests/qe/test_vault_gate.py -v`
|
|
310
|
+
Expected: PASS. `_run` has no references (`grep -n "_run\b" scripts/qe/vault_gate.py` shows only the `_loom._default_run`-unrelated absence — i.e. no `def _run`/`_run(` calls).
|
|
311
|
+
|
|
312
|
+
- [ ] **Step 6: Commit**
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
cd ~/Projects/wicked-garden
|
|
316
|
+
git add scripts/qe/vault_gate.py tests/qe/test_loom_gate_contract.py tests/qe/test_vault_gate.py
|
|
317
|
+
git commit -m "feat(loom-contract): cross_check loom-authoritative; delete in-process re-derivation body + _run"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Task 2: Contract the `resolve` surface — `resolve_vault(allow_npx=True)` loom-authoritative
|
|
323
|
+
|
|
324
|
+
**Files:**
|
|
325
|
+
- Modify: `~/Projects/wicked-garden/scripts/qe/vault_gate.py` (`resolve_vault`)
|
|
326
|
+
- Replace: `~/Projects/wicked-garden/tests/crew/test_loom_resolve_contract.py`
|
|
327
|
+
|
|
328
|
+
The cutover's `resolve_vault` head tries loom (when `allow_npx and use_loom()`) and on loom-error/unresolvable **falls through** to the in-process ladder. The contract makes loom authoritative for the `allow_npx=True` path: loom-unresolvable → None (no in-process npx fallback). The `allow_npx=False` path (which the loom head already skips) keeps the full in-process ladder unchanged — that is `vault_available()`'s concrete-install probe, which MUST NOT break.
|
|
329
|
+
|
|
330
|
+
- [ ] **Step 1: Rewrite the resolve contract test as loom-authoritative**
|
|
331
|
+
|
|
332
|
+
Replace `tests/crew/test_loom_resolve_contract.py`. The parity test (loom argv == in-process argv) is moot — there is no in-process argv on the `allow_npx=True` path. New assertions: loom authoritative for `allow_npx=True`; loom-unresolvable → None (NOT the npx ladder); `vault_available()`/`allow_npx=False` still resolves in-process (loom NOT consulted). Hermetic.
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
import os
|
|
336
|
+
import shutil
|
|
337
|
+
import sys
|
|
338
|
+
import unittest
|
|
339
|
+
from pathlib import Path
|
|
340
|
+
from unittest.mock import patch
|
|
341
|
+
|
|
342
|
+
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
343
|
+
for _p in (_REPO_ROOT / "scripts", _REPO_ROOT / "scripts" / "qe"):
|
|
344
|
+
if str(_p) not in sys.path:
|
|
345
|
+
sys.path.insert(0, str(_p))
|
|
346
|
+
|
|
347
|
+
import _loom # noqa: E402
|
|
348
|
+
import vault_gate as vg # noqa: E402
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class ResolveLoomAuthoritative(unittest.TestCase):
|
|
352
|
+
"""Contract phase: loom `resolve vault` is the SOLE resolution path for the
|
|
353
|
+
run-the-vault case (allow_npx=True). The in-process npx ladder fallback is
|
|
354
|
+
gone. The allow_npx=False concrete-install probe (vault_available) STAYS
|
|
355
|
+
in-process and never consults loom."""
|
|
356
|
+
|
|
357
|
+
def setUp(self):
|
|
358
|
+
for v in ("WICKED_VAULT_BIN", "WICKED_LOOM_BIN"):
|
|
359
|
+
os.environ.pop(v, None)
|
|
360
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "on"
|
|
361
|
+
|
|
362
|
+
def tearDown(self):
|
|
363
|
+
for v in ("WICKED_VAULT_BIN", "WICKED_LOOM_BIN", "WICKED_LOOM_CUTOVER"):
|
|
364
|
+
os.environ.pop(v, None)
|
|
365
|
+
|
|
366
|
+
def test_loom_resolve_is_authoritative(self):
|
|
367
|
+
def fake_run(prefix, args, timeout):
|
|
368
|
+
return {"exit_code": 0,
|
|
369
|
+
"stdout": '{"peer":"vault","command":["npx","--yes","wicked-vault"]}',
|
|
370
|
+
"stderr": "", "error": None}
|
|
371
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
372
|
+
patch.object(_loom, "_default_run", fake_run):
|
|
373
|
+
self.assertEqual(vg.resolve_vault(), ["npx", "--yes", "wicked-vault"])
|
|
374
|
+
|
|
375
|
+
def test_loom_command_null_surfaces_as_none(self):
|
|
376
|
+
# loom reports the vault kill-switch / unresolvable -> command=null -> None.
|
|
377
|
+
def fake_run(prefix, args, timeout):
|
|
378
|
+
return {"exit_code": 1, "stdout": '{"peer":"vault","command":null}',
|
|
379
|
+
"stderr": "", "error": None}
|
|
380
|
+
with patch.object(_loom, "resolve_loom", return_value=["wicked-loom"]), \
|
|
381
|
+
patch.object(_loom, "_default_run", fake_run):
|
|
382
|
+
self.assertIsNone(vg.resolve_vault())
|
|
383
|
+
|
|
384
|
+
def test_loom_unresolvable_returns_none_no_in_process_npx(self):
|
|
385
|
+
# auto + loom unresolvable. There is NO in-process npx ladder on the
|
|
386
|
+
# allow_npx=True path now -> resolve_vault returns None (fail-closed).
|
|
387
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "auto"
|
|
388
|
+
with patch.object(_loom, "resolve_loom", return_value=None), \
|
|
389
|
+
patch.object(shutil, "which",
|
|
390
|
+
side_effect=lambda b: "/usr/bin/npx" if b == "npx" else None):
|
|
391
|
+
self.assertIsNone(vg.resolve_vault())
|
|
392
|
+
|
|
393
|
+
def test_off_disables_loom_path_returns_none(self):
|
|
394
|
+
# off = emergency disable. allow_npx=True path no longer has an
|
|
395
|
+
# in-process fallback -> None (the gate then fails closed downstream).
|
|
396
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "off"
|
|
397
|
+
with patch.object(shutil, "which",
|
|
398
|
+
side_effect=lambda b: "/usr/bin/npx" if b == "npx" else None):
|
|
399
|
+
self.assertIsNone(vg.resolve_vault())
|
|
400
|
+
|
|
401
|
+
def test_vault_available_probe_stays_in_process(self):
|
|
402
|
+
# allow_npx=False is the concrete-install probe: loom is NEVER consulted
|
|
403
|
+
# (loom would report the npx last-resort as resolvable, corrupting the
|
|
404
|
+
# "installed" signal). A concrete PATH install resolves in-process.
|
|
405
|
+
with patch.object(_loom, "resolve_loom",
|
|
406
|
+
side_effect=AssertionError("loom must not be consulted")), \
|
|
407
|
+
patch.object(shutil, "which",
|
|
408
|
+
side_effect=lambda b: "/usr/local/bin/wicked-vault"
|
|
409
|
+
if b == "wicked-vault" else None):
|
|
410
|
+
self.assertEqual(vg.resolve_vault(allow_npx=False), ["/usr/local/bin/wicked-vault"])
|
|
411
|
+
self.assertTrue(vg.vault_available())
|
|
412
|
+
|
|
413
|
+
def test_vault_available_false_for_npx_only(self):
|
|
414
|
+
os.environ["WICKED_LOOM_CUTOVER"] = "on"
|
|
415
|
+
with patch.object(_loom, "resolve_loom",
|
|
416
|
+
side_effect=AssertionError("loom must not be consulted")), \
|
|
417
|
+
patch.object(shutil, "which",
|
|
418
|
+
side_effect=lambda b: "/usr/bin/npx" if b == "npx" else None):
|
|
419
|
+
self.assertFalse(vg.vault_available())
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
if __name__ == "__main__":
|
|
423
|
+
unittest.main()
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
- [ ] **Step 2: Run the test — confirm it fails**
|
|
427
|
+
|
|
428
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/crew/test_loom_resolve_contract.py -v`
|
|
429
|
+
Expected: `test_loom_unresolvable_returns_none_no_in_process_npx` and `test_off_disables_loom_path_returns_none` FAIL — current `resolve_vault` falls through to the in-process npx ladder and returns `["npx","--yes","wicked-vault"]` instead of None.
|
|
430
|
+
|
|
431
|
+
- [ ] **Step 3: Edit `resolve_vault` — loom authoritative for `allow_npx=True`, keep in-process ladder for `allow_npx=False`**
|
|
432
|
+
|
|
433
|
+
The cutover head is:
|
|
434
|
+
```python
|
|
435
|
+
if _loom is not None and allow_npx and _loom.use_loom(project_dir=project_dir):
|
|
436
|
+
out = _loom.run_json(["resolve", "vault"], project_dir=project_dir)
|
|
437
|
+
if out["error"] is None and isinstance(out.get("json"), dict):
|
|
438
|
+
return out["json"].get("command") # list[str] or None
|
|
439
|
+
# loom errored -> fall through to in-process (transition fail-soft).
|
|
440
|
+
```
|
|
441
|
+
Replace it so that on the `allow_npx=True` path loom is **authoritative** — loom-unresolvable/error → return None (do NOT fall through). Restructure `resolve_vault` so the in-process ladder ONLY runs for the concrete-install probe (`allow_npx=False`):
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
def resolve_vault(*, allow_npx: bool = True,
|
|
445
|
+
project_dir: Optional[Path] = None) -> Optional[List[str]]:
|
|
446
|
+
"""Return the argv prefix that invokes wicked-vault, or None.
|
|
447
|
+
|
|
448
|
+
Two distinct callers, two paths (contract phase):
|
|
449
|
+
|
|
450
|
+
- ``allow_npx=True`` (default; the run-the-vault path): **loom is
|
|
451
|
+
authoritative.** Garden asks ``wicked-loom resolve vault`` and returns
|
|
452
|
+
its ``command`` array (None when loom reports the vault unresolvable /
|
|
453
|
+
kill-switched). loom unresolvable, disabled (``WICKED_LOOM_CUTOVER=off``),
|
|
454
|
+
or erroring → None. There is no in-process npx ladder here anymore (the
|
|
455
|
+
contract removed it); a missing loom means a missing resolver, and the
|
|
456
|
+
gate downstream fails closed (I2).
|
|
457
|
+
|
|
458
|
+
- ``allow_npx=False`` (the ``vault_available`` concrete-install probe):
|
|
459
|
+
the in-process ladder (env → config → PATH → node_modules; **no npx, no
|
|
460
|
+
loom**). loom is deliberately NOT consulted: it would report the npx
|
|
461
|
+
last-resort as resolvable and corrupt the "is a concrete vault actually
|
|
462
|
+
installed?" signal. This path is unchanged from before the contract.
|
|
463
|
+
"""
|
|
464
|
+
if allow_npx:
|
|
465
|
+
# Run-the-vault path: loom authoritative.
|
|
466
|
+
if _loom is None or not _loom.use_loom(project_dir=project_dir):
|
|
467
|
+
return None # loom disabled / unresolvable -> no resolver
|
|
468
|
+
out = _loom.run_json(["resolve", "vault"], project_dir=project_dir)
|
|
469
|
+
if out["error"] is not None or not isinstance(out.get("json"), dict):
|
|
470
|
+
return None # loom errored -> fail-closed (no in-process fallback)
|
|
471
|
+
return out["json"].get("command") # list[str] or None
|
|
472
|
+
|
|
473
|
+
# Concrete-install probe (allow_npx=False): in-process ladder, no loom/npx.
|
|
474
|
+
if "WICKED_VAULT_BIN" in os.environ:
|
|
475
|
+
env = os.environ["WICKED_VAULT_BIN"].strip()
|
|
476
|
+
return _argv_for(env) if env else None # empty == kill-switch
|
|
477
|
+
|
|
478
|
+
pref = _read_config_preference("wicked-vault")
|
|
479
|
+
if pref:
|
|
480
|
+
return _argv_for(pref)
|
|
481
|
+
|
|
482
|
+
found = shutil.which("wicked-vault")
|
|
483
|
+
if found:
|
|
484
|
+
return [found]
|
|
485
|
+
|
|
486
|
+
base = Path(project_dir) if project_dir else Path.cwd()
|
|
487
|
+
local = base / "node_modules" / ".bin" / "wicked-vault"
|
|
488
|
+
if local.exists():
|
|
489
|
+
return [str(local)]
|
|
490
|
+
|
|
491
|
+
return None
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
> Implementer note: this DELETES the in-process `npx --yes wicked-vault` last-resort entirely from the `allow_npx=True` path (it was the redundant fallback the loom head shimmed). The `allow_npx=False` ladder never had an npx tier anyway (that is the point of the probe), so it is byte-for-byte the old logic minus the npx branch. `vault_available()` (calls `allow_npx=False`) is unchanged. The module docstring's "Resolution order" section (lines 22–35) should be updated to describe the two-path split; do that for honesty (R-docs), it is prose only.
|
|
495
|
+
|
|
496
|
+
- [ ] **Step 4: Update the module docstring's resolution-order section**
|
|
497
|
+
|
|
498
|
+
The `resolve_vault` docstring (above) is now authoritative; trim/rewrite the top-of-file "Resolution order for the vault CLI" block (lines ~22–35) to point at the two-path split rather than the old 5-tier ladder. Prose only; keep it short.
|
|
499
|
+
|
|
500
|
+
- [ ] **Step 5: Run the resolve tests + the gate tests + the loom resolver unit tests**
|
|
501
|
+
|
|
502
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/crew/test_loom_resolve_contract.py tests/crew/test_loom_resolver.py tests/qe/ -v`
|
|
503
|
+
Expected: PASS. (`test_loom_resolver.py` tests `_loom.resolve_loom` itself — unchanged, still green.)
|
|
504
|
+
|
|
505
|
+
- [ ] **Step 6: Commit**
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
cd ~/Projects/wicked-garden
|
|
509
|
+
git add scripts/qe/vault_gate.py tests/crew/test_loom_resolve_contract.py
|
|
510
|
+
git commit -m "feat(loom-contract): resolve_vault loom-authoritative for allow_npx=True; vault_available stays in-process"
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## Task 3: Update `_loom.py` docstrings — `off` no longer selects an in-process path
|
|
516
|
+
|
|
517
|
+
**Files:**
|
|
518
|
+
- Modify: `~/Projects/wicked-garden/scripts/_loom.py` (docstrings only — NO logic change)
|
|
519
|
+
|
|
520
|
+
`_loom.use_loom`/`cutover_mode` logic is unchanged (the flag stays `{auto,on,off}`). But the docstrings describe `off`/`auto` as "fall back to the in-process path" — which no longer exists for the shimmed surfaces. Correct the prose so the flag's meaning is honest post-contract.
|
|
521
|
+
|
|
522
|
+
- [ ] **Step 1: Edit the module docstring + `cutover_mode`/`use_loom` docstrings**
|
|
523
|
+
|
|
524
|
+
In the module docstring (point 3), change the description of the flag from "lets every cutover try loom first and fall back to the in-process path (the transition default)" to reflect that loom is now load-bearing: `off` is an emergency disable that makes the shimmed surfaces (resolve, gate) unavailable → the gate fails closed; there is no in-process fallback to select. `auto` uses loom iff resolvable; `on` forces it.
|
|
525
|
+
|
|
526
|
+
In `use_loom`'s docstring, change "auto -> only when loom resolves (the transition default: fall back to the in-process path otherwise)." to "auto -> only when loom resolves. off/auto-unresolvable means the shimmed surface is unavailable (the gate fails closed); there is no in-process fallback after the contract phase."
|
|
527
|
+
|
|
528
|
+
> Implementer note: do NOT change `use_loom`'s return logic. `off`→False / `on`→True / `auto`→resolvable is still exactly right; the contract only changed what the *callers* do when `use_loom()` is False (return None / fail closed instead of running in-process). The flow surface (`phase_manager`) still uses `use_loom()` for its parity mirror — that is out of scope and unaffected. The `loom_available`/`resolve_loom`/`run_json` logic is untouched.
|
|
529
|
+
|
|
530
|
+
- [ ] **Step 2: Run the loom unit tests**
|
|
531
|
+
|
|
532
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/crew/test_loom_resolver.py -v`
|
|
533
|
+
Expected: PASS (docstring-only change; logic identical).
|
|
534
|
+
|
|
535
|
+
- [ ] **Step 3: Commit**
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
cd ~/Projects/wicked-garden
|
|
539
|
+
git add scripts/_loom.py
|
|
540
|
+
git commit -m "docs(loom-contract): _loom flag docstrings — off is emergency disable, no in-process fallback"
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Task 4: Drop the `WICKED_LOOM_CUTOVER=off` autouse default from conftest
|
|
546
|
+
|
|
547
|
+
**Files:**
|
|
548
|
+
- Modify: `~/Projects/wicked-garden/tests/conftest.py`
|
|
549
|
+
|
|
550
|
+
The autouse `_loom_cutover_default_off` fixture forced `off` for the whole suite so legacy in-process unit tests ran deterministically without shelling to a real loom via the npx auto-fallback. After the contract there is **no in-process gate/resolve path** to select with `off`, so the rationale is gone for the shimmed surfaces. The gate/resolve tests now drive the loom path via MOCKED runners (they set `WICKED_LOOM_CUTOVER=on` and patch `_loom.resolve_loom`/`_loom._default_run` in their own setUp — already done in Tasks 1–2). The flow parity tests set the flag in their own body too.
|
|
551
|
+
|
|
552
|
+
**The risk to manage:** removing the default means any test that calls `cross_check`/`resolve_vault`/`gate_satisfied` WITHOUT setting the flag and WITHOUT mocking loom would, under `auto` on a box with `npx`, try to shell a real loom (network, slow, non-deterministic). We must ensure no such test exists.
|
|
553
|
+
|
|
554
|
+
- [ ] **Step 1: Audit for unguarded callers**
|
|
555
|
+
|
|
556
|
+
Run: `cd ~/Projects/wicked-garden && grep -rln "cross_check\|gate_satisfied\|resolve_vault\|vault_available" tests/ --include="*.py"`
|
|
557
|
+
For each hit, confirm the test either (a) sets `WICKED_LOOM_CUTOVER` explicitly + mocks the loom runner, (b) uses `allow_npx=False` (in-process probe, no loom), or (c) sets the vault kill-switch / loom off so the call fails closed deterministically. The files in play: `tests/qe/test_vault_gate.py`, `tests/qe/test_loom_gate_contract.py`, `tests/crew/test_loom_resolve_contract.py`. (Tasks 1–2 already made these self-contained.) Fix any straggler found here before removing the fixture.
|
|
558
|
+
|
|
559
|
+
- [ ] **Step 2: Remove the autouse fixture**
|
|
560
|
+
|
|
561
|
+
Delete the `_loom_cutover_default_off` fixture (lines ~49–69) from `tests/conftest.py`. Keep `pytest_configure` (sys.path ordering) and `_reset_bus_emit_counters`. Leave a one-line provenance comment where the fixture was, noting the contract phase removed it (the shimmed surfaces have no in-process path to default to; tests now drive loom via mocked runners).
|
|
562
|
+
|
|
563
|
+
> Defense-in-depth (recommended): instead of an `off` default, add a NEW autouse fixture that sets `WICKED_LOOM_CUTOVER=off` ONLY as a guard against accidental real-loom subprocess calls — no. That reintroduces the exact ambiguity we are removing. Better: leave NO default, and rely on each loom-touching test owning its flag (the T3 isolation rule). If a future test forgets, it fails loudly (real-loom attempt errors hermetically because npx is usually absent in CI), which is the correct signal. Document this in the provenance comment.
|
|
564
|
+
|
|
565
|
+
- [ ] **Step 3: Run the FULL suite**
|
|
566
|
+
|
|
567
|
+
Run: `cd ~/Projects/wicked-garden && python3 -m pytest tests/ -q`
|
|
568
|
+
Expected: 0 failed. Report the new total (was 497; the gate + resolve contract tests changed count — parity tests removed, loom-authoritative tests added). If any test fails because it relied on the `off` default, fix it by giving it an explicit flag + mocked runner (T3), not by restoring the global default.
|
|
569
|
+
|
|
570
|
+
- [ ] **Step 4: Commit**
|
|
571
|
+
|
|
572
|
+
```bash
|
|
573
|
+
cd ~/Projects/wicked-garden
|
|
574
|
+
git add tests/conftest.py
|
|
575
|
+
git commit -m "test(loom-contract): drop WICKED_LOOM_CUTOVER=off autouse default; tests own the loom flag"
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## Task 5: Docs — loom is load-bearing for gating, not an optional shim
|
|
581
|
+
|
|
582
|
+
**Files:**
|
|
583
|
+
- Modify: `~/Projects/wicked-garden/docs/required-peers.md`
|
|
584
|
+
- Modify: `~/Projects/wicked-garden/hooks/scripts/bootstrap.py` (prose only — `_check_loom_dependency` message)
|
|
585
|
+
|
|
586
|
+
The cutover added the loom peer row with the bracketed clause "during cutover, garden falls back to its in-process runtime; after the contract phase, loom is the only runtime." Post-contract, the first half is no longer true for the shimmed surfaces. Tighten the prose so loom reads as load-bearing for gating + resolution.
|
|
587
|
+
|
|
588
|
+
- [ ] **Step 1: Edit `docs/required-peers.md`**
|
|
589
|
+
|
|
590
|
+
In the "## Why each is load-bearing" `wicked-loom` bullet, drop the cutover-fallback parenthetical. New ending: "Without it, the produces-gate has no re-derivation engine and fails closed — 'done' cannot be re-derived, so it cannot be asserted." Also update the loom table-row "What it does" cell if it implies optionality. Add a sentence (near the kill-switch paragraph) noting `WICKED_LOOM_CUTOVER=off` is now an *emergency disable* that fails the gate closed (no in-process fallback remains), parallel to the vault kill-switch.
|
|
591
|
+
|
|
592
|
+
- [ ] **Step 2: Edit `bootstrap.py` `_check_loom_dependency` message**
|
|
593
|
+
|
|
594
|
+
Change the return string's last clause from "(Cutover phase: garden falls back to its in-process runtime when loom is absent.)" to reflect that loom is now load-bearing: "(Gating + peer resolution route through loom; without it the produces-gate fails closed.)" Also update the function docstring's "During the cutover transition garden falls back to its in-process runtime when loom is absent" sentence the same way. Detection logic (PATH / skill-dir scan / fail-open) is UNCHANGED — only the prose.
|
|
595
|
+
|
|
596
|
+
> Implementer note: do NOT touch `_check_loom_dependency`'s control flow or the `WICKED_LOOM_BIN`/`WICKED_LOOM_CUTOVER=off` "don't nag" early-returns — those are still correct (an operator who set the kill-switch or disabled loom is driving deliberately). Only the human-facing message text changes.
|
|
597
|
+
|
|
598
|
+
- [ ] **Step 3: Verify + commit**
|
|
599
|
+
|
|
600
|
+
Run: `cd ~/Projects/wicked-garden && python3 -c "import ast; ast.parse(open('hooks/scripts/bootstrap.py').read()); print('bootstrap.py parses')" && grep -c "in-process" docs/required-peers.md`
|
|
601
|
+
Expected: parses; the `in-process` count in required-peers.md drops (the fallback claim is gone).
|
|
602
|
+
|
|
603
|
+
```bash
|
|
604
|
+
cd ~/Projects/wicked-garden
|
|
605
|
+
git add docs/required-peers.md hooks/scripts/bootstrap.py
|
|
606
|
+
git commit -m "docs(loom-contract): loom is load-bearing for gating; drop in-process-fallback prose"
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Final verification (HARD GATES — must all hold)
|
|
612
|
+
|
|
613
|
+
- [ ] **Full suite green:**
|
|
614
|
+
`cd ~/Projects/wicked-garden && python3 -m pytest tests/ -q` → **0 failed**. Report the new total vs the 497 baseline.
|
|
615
|
+
|
|
616
|
+
- [ ] **Fail-closed preserved (the load-bearing invariant):**
|
|
617
|
+
`python3 -m pytest tests/qe/test_loom_gate_contract.py -k "fails_closed or unavailable" -v` → the loom-unresolvable / loom-error / vault-absent cases all return `available: False` / `gate: "unavailable"` / `satisfied: false`. NEVER a pass.
|
|
618
|
+
|
|
619
|
+
- [ ] **Deletions are real (this is the contract — confirm, don't trust):**
|
|
620
|
+
`git diff main -- scripts/qe/vault_gate.py` shows the in-process `cross_check` body (`args = ["cross-check", ...]; run = _run(...); json.loads(...)`) and the `_run` helper REMOVED (red lines), and the `allow_npx=True` in-process npx ladder REMOVED from `resolve_vault`. `grep -n "def _run\|_run(" scripts/qe/vault_gate.py` → no in-process `_run` definition/call remains.
|
|
621
|
+
|
|
622
|
+
- [ ] **`vault_available()` intact:** `python3 -m pytest tests/crew/test_loom_resolve_contract.py -k vault_available -v` and `tests/qe/test_vault_gate.py -k Resolution` → green (the `allow_npx=False` probe still resolves a concrete install in-process; loom never consulted).
|
|
623
|
+
|
|
624
|
+
- [ ] **Hermetic:** no test spawns a real loom/npx. `grep -rn "npx\|subprocess.run\|_default_run" tests/qe/test_loom_gate_contract.py tests/crew/test_loom_resolve_contract.py` → only mocked runners / patched `_default_run`; the real-vault `VaultBackedGateTests` stays skip-guarded.
|
|
625
|
+
|
|
626
|
+
- [ ] **Push:** `git push -u origin feat/loom-contract`. Do NOT open a PR or merge (the parent reviews + merges).
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Self-review
|
|
631
|
+
|
|
632
|
+
- **Honest-evidence preserved (I2):** every deleted path is a *re-derivation engine*, never a *fail-closed guard*. The new `cross_check` returns `available: False` on loom-absent/error/unavailable; `gate_satisfied` is structurally unchanged and still fails closed. The fail-closed test is mandatory (final verification gate 2). No vacuous-pass path is introduced — verified by the `test_loom_error_fails_closed_no_in_process_pass` + `test_gate_satisfied_fails_closed_when_loom_absent` tests.
|
|
633
|
+
- **`vault_available()` not broken:** the `allow_npx=False` in-process ladder is preserved verbatim (minus the npx tier it never had). Loom is never consulted on that path — asserted by `test_vault_available_probe_stays_in_process` (patches `resolve_loom` to raise if touched). This is the explicit "keep what `vault_available()` needs" requirement.
|
|
634
|
+
- **Scope discipline:** only `resolve` + `gate` (the shimmed surfaces) are contracted. The flow parity mirror, `flow_compiler`, `phase_manager` storage, `_integration_resolver`/`_capability_resolver`/`_capability_registry`, `_bus`/`_event_*`, and the compiler are LEFT ALONE (spec §6 future increments). The flow contract tests are NOT touched.
|
|
635
|
+
- **Flag decision justified:** `WICKED_LOOM_CUTOVER` kept as a documented emergency disable that fails closed; logic unchanged, prose corrected. Removing it was rejected (free coarse kill-switch, parallel to the vault's).
|
|
636
|
+
- **Deletions are real, not flag flips:** Task 1 deletes the `_run` helper and the in-process `cross_check` body; Task 2 deletes the `allow_npx=True` npx ladder. The final verification gate diffs `main` to confirm red lines exist. R1 (no dead code) holds: `_run` is removed because its only caller is gone.
|
|
637
|
+
- **Tests stay hermetic + isolated (T1/T3):** every loom-touching test sets its own flag in setUp and patches the loom runner; the autouse global default is removed so isolation is per-test, not suite-global. No real npx/network.
|
|
638
|
+
- **R6 (no god functions):** `cross_check` shrinks (one path, not two); `resolve_vault` splits cleanly by `allow_npx` into the loom path and the probe ladder. Neither grows.
|