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,243 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""scripts/crew/_task_audit_writer.py — cross-session task-chain index.
|
|
3
|
+
|
|
4
|
+
Phase 3 of v10. Designed in jam session 03 (decision memory:
|
|
5
|
+
``v10-native-task-store-audit-trail-decision``).
|
|
6
|
+
|
|
7
|
+
This module owns the JSONL audit-log schema. **It is the SOLE writer**
|
|
8
|
+
of ``${CLAUDE_CONFIG_DIR}/wicked-garden/task-audit/{session_id}.jsonl``.
|
|
9
|
+
Schema changes require a PR to this module — anyone else writing to the
|
|
10
|
+
file is a layering violation.
|
|
11
|
+
|
|
12
|
+
## Purpose vs. dispatch-log
|
|
13
|
+
|
|
14
|
+
This is NOT the same as ``phases/{phase}/dispatch-log.jsonl``. The two
|
|
15
|
+
serve different threat models — see brain memory
|
|
16
|
+
``dispatch-log-precedes-reviewer-do-not-move-to-post-hook``:
|
|
17
|
+
|
|
18
|
+
* dispatch-log: HMAC-signed, written BEFORE reviewer invocation. Threat
|
|
19
|
+
model = rogue reviewer self-authorizing a gate-result. Sequencing is
|
|
20
|
+
the security property.
|
|
21
|
+
* task-audit (this file): plain JSONL, written AFTER native TaskCreate
|
|
22
|
+
by a PostToolUse hook. Threat model = ``verify_chain_emission`` not
|
|
23
|
+
finding tasks created in earlier sessions because the per-session
|
|
24
|
+
task store was never cross-session-indexed.
|
|
25
|
+
|
|
26
|
+
Different threat models, different files. Conflating them was an
|
|
27
|
+
explicit anti-pattern caught by the brainstorm.
|
|
28
|
+
|
|
29
|
+
## Schema (locked; rev with PR)
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
"ts": ISO 8601 UTC,
|
|
33
|
+
"session_id": sanitised CLAUDE_SESSION_ID,
|
|
34
|
+
"task_id": native task id from tool_input/tool_response,
|
|
35
|
+
"subject": task subject string,
|
|
36
|
+
"chain_id": metadata.chain_id (may be None),
|
|
37
|
+
"event_type": metadata.event_type (may be None),
|
|
38
|
+
"source_agent": metadata.source_agent (may be None),
|
|
39
|
+
"status": "pending" | "in_progress" | "completed",
|
|
40
|
+
"tool": "TaskCreate" | "TaskUpdate"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Fail-open: any I/O exception is swallowed. Missing audit entries surface
|
|
44
|
+
as drift in ``verify_chain_emission`` rather than a hook crash.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from __future__ import annotations
|
|
48
|
+
|
|
49
|
+
import json
|
|
50
|
+
import os
|
|
51
|
+
import re
|
|
52
|
+
import tempfile
|
|
53
|
+
from datetime import datetime, timezone
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
from typing import Any, Iterable
|
|
56
|
+
|
|
57
|
+
# Stable schema version. Bump on any change to the entry shape.
|
|
58
|
+
AUDIT_SCHEMA_VERSION = 1
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _audit_dir() -> Path:
|
|
62
|
+
"""Return the per-host audit directory.
|
|
63
|
+
|
|
64
|
+
Mirrors the Claude Code config dir layout. Falls back to a temp dir
|
|
65
|
+
if CLAUDE_CONFIG_DIR is not set, so the writer never raises.
|
|
66
|
+
"""
|
|
67
|
+
base = (
|
|
68
|
+
os.environ.get("CLAUDE_CONFIG_DIR")
|
|
69
|
+
or str(Path.home() / ".claude")
|
|
70
|
+
)
|
|
71
|
+
return Path(base) / "wicked-garden" / "task-audit"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_SAFE_ID_RE = re.compile(r"[^A-Za-z0-9_.\-]")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _sanitize_session_id(raw: str | None) -> str:
|
|
78
|
+
"""Strip path traversal and shell metacharacters from a session id."""
|
|
79
|
+
if not raw:
|
|
80
|
+
return "default"
|
|
81
|
+
cleaned = _SAFE_ID_RE.sub("_", raw)
|
|
82
|
+
return cleaned[:128] or "default"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _now_iso() -> str:
|
|
86
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _audit_file_for_session(session_id: str) -> Path:
|
|
90
|
+
return _audit_dir() / f"{_sanitize_session_id(session_id)}.jsonl"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _safe_get(d: Any, *keys: str, default: Any = None) -> Any:
|
|
94
|
+
"""Walk a nested dict; return default on any miss."""
|
|
95
|
+
cur = d
|
|
96
|
+
for k in keys:
|
|
97
|
+
if not isinstance(cur, dict):
|
|
98
|
+
return default
|
|
99
|
+
cur = cur.get(k)
|
|
100
|
+
if cur is None:
|
|
101
|
+
return default
|
|
102
|
+
return cur
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _build_entry(
|
|
106
|
+
*,
|
|
107
|
+
tool_name: str,
|
|
108
|
+
tool_input: dict | None,
|
|
109
|
+
tool_response: Any,
|
|
110
|
+
session_id: str,
|
|
111
|
+
) -> dict | None:
|
|
112
|
+
"""Compose the JSONL entry dict for one tool call.
|
|
113
|
+
|
|
114
|
+
Returns None when the tool call is not a task-management event we
|
|
115
|
+
care about (caller skips writing). The caller is expected to have
|
|
116
|
+
already gated on ``tool_name in {"TaskCreate", "TaskUpdate"}``;
|
|
117
|
+
this function double-checks defensively.
|
|
118
|
+
"""
|
|
119
|
+
if tool_name not in ("TaskCreate", "TaskUpdate"):
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
ti = tool_input or {}
|
|
123
|
+
metadata = ti.get("metadata") if isinstance(ti.get("metadata"), dict) else {}
|
|
124
|
+
|
|
125
|
+
# Native task ids appear in different shapes depending on tool. For
|
|
126
|
+
# TaskCreate, the response carries the new id; for TaskUpdate, the
|
|
127
|
+
# input carries taskId. Tolerate both for either tool.
|
|
128
|
+
task_id = (
|
|
129
|
+
ti.get("taskId")
|
|
130
|
+
or ti.get("task_id")
|
|
131
|
+
or _safe_get(tool_response, "taskId")
|
|
132
|
+
or _safe_get(tool_response, "task_id")
|
|
133
|
+
or _safe_get(tool_response, "id")
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Status: TaskCreate defaults to pending. TaskUpdate may carry an
|
|
137
|
+
# explicit status; if absent we still record the update so future
|
|
138
|
+
# consumers can correlate the timestamp with the underlying file.
|
|
139
|
+
if tool_name == "TaskCreate":
|
|
140
|
+
status = ti.get("status") or "pending"
|
|
141
|
+
else:
|
|
142
|
+
status = ti.get("status") or "updated"
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
"schema_version": AUDIT_SCHEMA_VERSION,
|
|
146
|
+
"ts": _now_iso(),
|
|
147
|
+
"session_id": _sanitize_session_id(session_id),
|
|
148
|
+
"task_id": task_id,
|
|
149
|
+
"subject": ti.get("subject"),
|
|
150
|
+
"chain_id": metadata.get("chain_id"),
|
|
151
|
+
"event_type": metadata.get("event_type"),
|
|
152
|
+
"source_agent": metadata.get("source_agent"),
|
|
153
|
+
"phase": metadata.get("phase"),
|
|
154
|
+
"status": status,
|
|
155
|
+
"tool": tool_name,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def append_task_audit(
|
|
160
|
+
*,
|
|
161
|
+
tool_name: str,
|
|
162
|
+
tool_input: dict | None,
|
|
163
|
+
tool_response: Any,
|
|
164
|
+
session_id: str,
|
|
165
|
+
) -> bool:
|
|
166
|
+
"""Append one task-audit JSONL entry for a TaskCreate/TaskUpdate call.
|
|
167
|
+
|
|
168
|
+
Returns True when an entry was written. Returns False on filtered
|
|
169
|
+
tool names or on any I/O error (fail-open). Never raises.
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
entry = _build_entry(
|
|
173
|
+
tool_name=tool_name,
|
|
174
|
+
tool_input=tool_input,
|
|
175
|
+
tool_response=tool_response,
|
|
176
|
+
session_id=session_id,
|
|
177
|
+
)
|
|
178
|
+
if entry is None:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
path = _audit_file_for_session(session_id)
|
|
182
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
line = json.dumps(entry, separators=(",", ":")) + "\n"
|
|
184
|
+
with path.open("a", encoding="utf-8") as fh:
|
|
185
|
+
fh.write(line)
|
|
186
|
+
return True
|
|
187
|
+
except Exception:
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def scan_chain(chain_id: str) -> list[dict]:
|
|
192
|
+
"""Scan all session audit files for entries matching ``chain_id``.
|
|
193
|
+
|
|
194
|
+
Returns a list of entry dicts (newest first). Empty list when the
|
|
195
|
+
audit directory does not exist yet — the writer hasn't fired or
|
|
196
|
+
PostToolUse is disabled. ``verify_chain_emission`` uses this as a
|
|
197
|
+
cross-session fallback when the native per-session task scan misses.
|
|
198
|
+
|
|
199
|
+
O(N sessions) directory walk. At ~100 sessions × ~50 entries this
|
|
200
|
+
is a few KB scan and well under 10ms in practice.
|
|
201
|
+
"""
|
|
202
|
+
audit_dir = _audit_dir()
|
|
203
|
+
if not audit_dir.is_dir():
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
entries: list[dict] = []
|
|
207
|
+
for path in sorted(audit_dir.glob("*.jsonl")):
|
|
208
|
+
try:
|
|
209
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
210
|
+
for line in fh:
|
|
211
|
+
line = line.strip()
|
|
212
|
+
if not line:
|
|
213
|
+
continue
|
|
214
|
+
try:
|
|
215
|
+
e = json.loads(line)
|
|
216
|
+
except json.JSONDecodeError:
|
|
217
|
+
continue
|
|
218
|
+
if e.get("chain_id") == chain_id:
|
|
219
|
+
entries.append(e)
|
|
220
|
+
except OSError:
|
|
221
|
+
continue
|
|
222
|
+
# Newest first by timestamp; fall back to insertion order for
|
|
223
|
+
# entries lacking a sortable ts.
|
|
224
|
+
entries.sort(key=lambda e: e.get("ts") or "", reverse=True)
|
|
225
|
+
return entries
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def latest_per_task(entries: Iterable[dict]) -> list[dict]:
|
|
229
|
+
"""Collapse multiple entries per task_id to the most recent one.
|
|
230
|
+
|
|
231
|
+
Useful for ``verify_chain_emission``: a TaskCreate followed by a
|
|
232
|
+
TaskUpdate for the same id yields two entries; we want one row per
|
|
233
|
+
task with the latest known status.
|
|
234
|
+
"""
|
|
235
|
+
out: dict[str, dict] = {}
|
|
236
|
+
for e in entries:
|
|
237
|
+
tid = e.get("task_id")
|
|
238
|
+
if not tid:
|
|
239
|
+
continue
|
|
240
|
+
prev = out.get(tid)
|
|
241
|
+
if prev is None or (e.get("ts") or "") > (prev.get("ts") or ""):
|
|
242
|
+
out[tid] = e
|
|
243
|
+
return list(out.values())
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"""Task-store reader abstraction — #596 v8-PR-2.
|
|
2
|
+
|
|
3
|
+
Centralises the WG_DAEMON_ENABLED routing logic for all native task-store
|
|
4
|
+
readers. Callers that previously read
|
|
5
|
+
``${CLAUDE_CONFIG_DIR:-~/.claude}/tasks/{session_id}/*.json`` directly now
|
|
6
|
+
call helpers from this module and the routing is handled transparently.
|
|
7
|
+
|
|
8
|
+
WG_DAEMON_ENABLED modes:
|
|
9
|
+
false (default) — direct file read; behaviour identical to v7.2.x
|
|
10
|
+
true — try daemon HTTP first; fall back to direct file read on error
|
|
11
|
+
always — daemon HTTP only; error surfaces to caller on unreachable
|
|
12
|
+
|
|
13
|
+
The module-level ``_DAEMON_MODE`` constant is resolved once at import time
|
|
14
|
+
so every process makes exactly one env-var decision (no per-call overhead).
|
|
15
|
+
|
|
16
|
+
Performance:
|
|
17
|
+
- Direct-file path: unchanged vs pre-PR-2
|
|
18
|
+
- Daemon path (WG_DAEMON_ENABLED=true): single HTTP GET with a 45ms timeout
|
|
19
|
+
(under the 50ms p99 hard requirement on the hot subagent_lifecycle path)
|
|
20
|
+
- Daemon path (WG_DAEMON_ENABLED=always): same timeout; error on unreachable
|
|
21
|
+
|
|
22
|
+
Cold-start behavior:
|
|
23
|
+
The first call after daemon startup (or after daemon restart) will hit the
|
|
24
|
+
45ms HTTP timeout and fall back to the direct file read. This is intentional
|
|
25
|
+
and correct — the daemon needs one request to warm its connection pool and
|
|
26
|
+
prime the projection cursor. Subsequent warm-path calls are ~9ms p99.
|
|
27
|
+
|
|
28
|
+
Operators running WG_DAEMON_ENABLED=always should know: a daemon crash or
|
|
29
|
+
restart will surface a 45ms-latency blip on the next SubagentStart before
|
|
30
|
+
the next request warms the path. WG_DAEMON_ENABLED=true (default) falls
|
|
31
|
+
back to file read silently, so is blip-free at the cost of one extra
|
|
32
|
+
file-scan per restart cycle.
|
|
33
|
+
|
|
34
|
+
R3: all constants named.
|
|
35
|
+
R5: explicit timeout on every HTTP call; bounded iterdir scan preserved.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import os
|
|
42
|
+
import urllib.error
|
|
43
|
+
import urllib.request
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
from typing import Any
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# Constants
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
_ENV_DAEMON_ENABLED = "WG_DAEMON_ENABLED"
|
|
52
|
+
_ENV_DAEMON_PORT = "WG_DAEMON_PORT"
|
|
53
|
+
_ENV_DAEMON_HOST = "WG_DAEMON_HOST"
|
|
54
|
+
_ENV_CONFIG_DIR = "CLAUDE_CONFIG_DIR"
|
|
55
|
+
|
|
56
|
+
# Default port matches daemon/server.py DEFAULT_PORT.
|
|
57
|
+
_DEFAULT_DAEMON_PORT: int = 4244
|
|
58
|
+
_DEFAULT_DAEMON_HOST: str = "127.0.0.1"
|
|
59
|
+
|
|
60
|
+
# Sub-50ms hard requirement per #596 spec. We use 45ms to leave headroom.
|
|
61
|
+
# This is a per-call ceiling; network is loopback-only so the budget is realistic.
|
|
62
|
+
_DAEMON_HTTP_TIMEOUT_S: float = 0.045
|
|
63
|
+
|
|
64
|
+
# How many in-progress tasks to surface in WIP-context reads (preserves
|
|
65
|
+
# existing limits in prompt_submit.py and pre_compact.py).
|
|
66
|
+
_DEFAULT_WIP_LIMIT: int = 10
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# Module-level mode resolution (R3, one decision per process)
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def _resolve_daemon_mode() -> str:
|
|
74
|
+
"""Return the normalised WG_DAEMON_ENABLED value: 'false' | 'true' | 'always'."""
|
|
75
|
+
raw = (os.environ.get(_ENV_DAEMON_ENABLED) or "false").strip().lower()
|
|
76
|
+
return raw if raw in ("true", "always") else "false"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
_DAEMON_MODE: str = _resolve_daemon_mode()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# Direct-file helpers (unchanged from pre-PR-2 behaviour)
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
def _tasks_dir_for_session(session_id: str) -> Path | None:
|
|
87
|
+
"""Resolve ``${CLAUDE_CONFIG_DIR:-~/.claude}/tasks/{session_id}/``."""
|
|
88
|
+
config_dir = os.environ.get(_ENV_CONFIG_DIR)
|
|
89
|
+
base = Path(config_dir) if config_dir else Path.home() / ".claude"
|
|
90
|
+
tasks_dir = base / "tasks" / session_id
|
|
91
|
+
return tasks_dir if tasks_dir.is_dir() else None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _read_task_file(session_id: str, task_id: str) -> dict | None:
|
|
95
|
+
"""Read a single task JSON file. Returns None on any error."""
|
|
96
|
+
if not session_id or not task_id:
|
|
97
|
+
return None
|
|
98
|
+
try:
|
|
99
|
+
config_dir = os.environ.get(_ENV_CONFIG_DIR)
|
|
100
|
+
base = Path(config_dir) if config_dir else Path.home() / ".claude"
|
|
101
|
+
task_file = base / "tasks" / session_id / f"{task_id}.json"
|
|
102
|
+
if not task_file.is_file():
|
|
103
|
+
return None
|
|
104
|
+
data = json.loads(task_file.read_text(encoding="utf-8"))
|
|
105
|
+
return data if isinstance(data, dict) else None
|
|
106
|
+
except Exception:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _scan_tasks_dir(
|
|
111
|
+
session_id: str,
|
|
112
|
+
*,
|
|
113
|
+
status_filter: str | None = None,
|
|
114
|
+
limit: int = _DEFAULT_WIP_LIMIT,
|
|
115
|
+
) -> list[dict]:
|
|
116
|
+
"""Scan the tasks dir and return task dicts matching optional status_filter.
|
|
117
|
+
|
|
118
|
+
Returns at most ``limit`` entries; skips hidden files and non-JSON entries.
|
|
119
|
+
Never raises — returns [] on any error.
|
|
120
|
+
"""
|
|
121
|
+
if not session_id:
|
|
122
|
+
return []
|
|
123
|
+
out: list[dict] = []
|
|
124
|
+
try:
|
|
125
|
+
tasks_dir = _tasks_dir_for_session(session_id)
|
|
126
|
+
if tasks_dir is None:
|
|
127
|
+
return out
|
|
128
|
+
for entry in tasks_dir.iterdir():
|
|
129
|
+
if entry.name.startswith(".") or entry.suffix != ".json":
|
|
130
|
+
continue
|
|
131
|
+
try:
|
|
132
|
+
data = json.loads(entry.read_text(encoding="utf-8"))
|
|
133
|
+
except Exception:
|
|
134
|
+
continue
|
|
135
|
+
if not isinstance(data, dict):
|
|
136
|
+
continue
|
|
137
|
+
if status_filter is not None and data.get("status") != status_filter:
|
|
138
|
+
continue
|
|
139
|
+
out.append(data)
|
|
140
|
+
if len(out) >= limit:
|
|
141
|
+
break
|
|
142
|
+
except Exception:
|
|
143
|
+
pass # fail open — tasks dir absent or unreadable; return whatever we collected
|
|
144
|
+
return out
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# Daemon HTTP helpers
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
def _daemon_base_url() -> str:
|
|
152
|
+
host = os.environ.get(_ENV_DAEMON_HOST, _DEFAULT_DAEMON_HOST)
|
|
153
|
+
port_raw = os.environ.get(_ENV_DAEMON_PORT, str(_DEFAULT_DAEMON_PORT))
|
|
154
|
+
try:
|
|
155
|
+
port = int(port_raw)
|
|
156
|
+
except (ValueError, TypeError):
|
|
157
|
+
port = _DEFAULT_DAEMON_PORT
|
|
158
|
+
return f"http://{host}:{port}"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _daemon_get(path: str) -> Any | None:
|
|
162
|
+
"""Issue a GET request to the daemon at ``path``.
|
|
163
|
+
|
|
164
|
+
Returns the parsed JSON body on 200, raises ``urllib.error.URLError``
|
|
165
|
+
or ``urllib.error.HTTPError`` on failure. Callers catch accordingly.
|
|
166
|
+
"""
|
|
167
|
+
url = _daemon_base_url() + path
|
|
168
|
+
req = urllib.request.Request(url, method="GET")
|
|
169
|
+
with urllib.request.urlopen(req, timeout=_DAEMON_HTTP_TIMEOUT_S) as resp:
|
|
170
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
# Public API — routed readers
|
|
175
|
+
# ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
def get_task_metadata(session_id: str, task_id: str) -> dict | None:
|
|
178
|
+
"""Return the ``metadata`` dict from a single task.
|
|
179
|
+
|
|
180
|
+
Routes via WG_DAEMON_ENABLED:
|
|
181
|
+
false → direct file read
|
|
182
|
+
true → daemon HTTP; fallback to direct file on error
|
|
183
|
+
always → daemon HTTP; returns None on unreachable
|
|
184
|
+
"""
|
|
185
|
+
if _DAEMON_MODE == "false":
|
|
186
|
+
data = _read_task_file(session_id, task_id)
|
|
187
|
+
if data is None:
|
|
188
|
+
return None
|
|
189
|
+
meta = data.get("metadata")
|
|
190
|
+
return meta if isinstance(meta, dict) else None
|
|
191
|
+
|
|
192
|
+
# Daemon path.
|
|
193
|
+
try:
|
|
194
|
+
row = _daemon_get(f"/tasks/{task_id}")
|
|
195
|
+
if row and isinstance(row, dict):
|
|
196
|
+
meta = row.get("metadata")
|
|
197
|
+
return meta if isinstance(meta, dict) else None
|
|
198
|
+
return None
|
|
199
|
+
except Exception:
|
|
200
|
+
if _DAEMON_MODE == "always":
|
|
201
|
+
return None
|
|
202
|
+
# fallback
|
|
203
|
+
data = _read_task_file(session_id, task_id)
|
|
204
|
+
if data is None:
|
|
205
|
+
return None
|
|
206
|
+
meta = data.get("metadata")
|
|
207
|
+
return meta if isinstance(meta, dict) else None
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_active_task_event_type(session_id: str) -> str | None:
|
|
211
|
+
"""Return event_type of the most-recently-modified in_progress task.
|
|
212
|
+
|
|
213
|
+
This is the hot path — called on every SubagentStart. Sub-50ms p99 is
|
|
214
|
+
required for the daemon path.
|
|
215
|
+
|
|
216
|
+
Routes via WG_DAEMON_ENABLED:
|
|
217
|
+
false → direct file scan (existing behaviour, bit-identical)
|
|
218
|
+
true → daemon GET /tasks?session=<id>&status=in_progress; fallback to direct
|
|
219
|
+
always → daemon only; None on unreachable
|
|
220
|
+
"""
|
|
221
|
+
if not session_id:
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
if _DAEMON_MODE == "false":
|
|
225
|
+
return _get_active_task_event_type_direct(session_id)
|
|
226
|
+
|
|
227
|
+
# Daemon path.
|
|
228
|
+
try:
|
|
229
|
+
rows = _daemon_get(f"/tasks?session={session_id}&status=in_progress&limit=10")
|
|
230
|
+
if not rows or not isinstance(rows, list):
|
|
231
|
+
return None
|
|
232
|
+
# Pick the task with the highest updated_at (proxy for most-recent).
|
|
233
|
+
best = max(rows, key=lambda r: r.get("updated_at", 0), default=None)
|
|
234
|
+
if best is None:
|
|
235
|
+
return None
|
|
236
|
+
return best.get("event_type")
|
|
237
|
+
except Exception:
|
|
238
|
+
if _DAEMON_MODE == "always":
|
|
239
|
+
return None
|
|
240
|
+
return _get_active_task_event_type_direct(session_id)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _get_active_task_event_type_direct(session_id: str) -> str | None:
|
|
244
|
+
"""Direct-file fallback for get_active_task_event_type (pre-PR-2 behaviour)."""
|
|
245
|
+
try:
|
|
246
|
+
tasks_dir = _tasks_dir_for_session(session_id)
|
|
247
|
+
if tasks_dir is None:
|
|
248
|
+
return None
|
|
249
|
+
best_mtime = -1.0
|
|
250
|
+
best_event_type: str | None = None
|
|
251
|
+
for entry in tasks_dir.iterdir():
|
|
252
|
+
if entry.name.startswith(".") or entry.suffix != ".json":
|
|
253
|
+
continue
|
|
254
|
+
try:
|
|
255
|
+
data = json.loads(entry.read_text(encoding="utf-8"))
|
|
256
|
+
except Exception:
|
|
257
|
+
continue
|
|
258
|
+
if not isinstance(data, dict) or data.get("status") != "in_progress":
|
|
259
|
+
continue
|
|
260
|
+
mtime = entry.stat().st_mtime
|
|
261
|
+
if mtime > best_mtime:
|
|
262
|
+
best_mtime = mtime
|
|
263
|
+
metadata = data.get("metadata") or {}
|
|
264
|
+
if isinstance(metadata, dict):
|
|
265
|
+
best_event_type = metadata.get("event_type")
|
|
266
|
+
return best_event_type
|
|
267
|
+
except Exception:
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def list_in_progress_tasks(
|
|
272
|
+
session_id: str,
|
|
273
|
+
limit: int = _DEFAULT_WIP_LIMIT,
|
|
274
|
+
) -> list[str]:
|
|
275
|
+
"""Return subject strings of in-progress tasks (for WIP context injection).
|
|
276
|
+
|
|
277
|
+
Used by prompt_submit.py and pre_compact.py for context display — subjects
|
|
278
|
+
only, not full task dicts.
|
|
279
|
+
"""
|
|
280
|
+
if _DAEMON_MODE == "false":
|
|
281
|
+
tasks = _scan_tasks_dir(session_id, status_filter="in_progress", limit=limit)
|
|
282
|
+
return [t.get("subject") or "untitled" for t in tasks]
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
rows = _daemon_get(
|
|
286
|
+
f"/tasks?session={session_id}&status=in_progress&limit={limit}"
|
|
287
|
+
)
|
|
288
|
+
if not rows or not isinstance(rows, list):
|
|
289
|
+
return []
|
|
290
|
+
return [r.get("subject") or "untitled" for r in rows]
|
|
291
|
+
except Exception:
|
|
292
|
+
if _DAEMON_MODE == "always":
|
|
293
|
+
return []
|
|
294
|
+
tasks = _scan_tasks_dir(session_id, status_filter="in_progress", limit=limit)
|
|
295
|
+
return [t.get("subject") or "untitled" for t in tasks]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def collect_tasks_for_chain(chain_id: str) -> list[dict]:
|
|
299
|
+
"""Return task dicts whose metadata.chain_id matches ``chain_id``.
|
|
300
|
+
|
|
301
|
+
Used by current_chain.py and verify_chain_emission.py.
|
|
302
|
+
|
|
303
|
+
Routes via WG_DAEMON_ENABLED:
|
|
304
|
+
false → direct file scan across all sessions (existing behaviour)
|
|
305
|
+
true → daemon GET /tasks?chain_id=<chain_id>; fallback to direct
|
|
306
|
+
always → daemon only; [] on unreachable
|
|
307
|
+
"""
|
|
308
|
+
if _DAEMON_MODE == "false":
|
|
309
|
+
return _collect_tasks_for_chain_direct(chain_id)
|
|
310
|
+
|
|
311
|
+
try:
|
|
312
|
+
rows = _daemon_get(f"/tasks?chain_id={chain_id}&limit=500")
|
|
313
|
+
if not rows or not isinstance(rows, list):
|
|
314
|
+
return []
|
|
315
|
+
# Re-shape to match the dict shape callers expect (id, subject, status, metadata).
|
|
316
|
+
result = []
|
|
317
|
+
for row in rows:
|
|
318
|
+
meta = row.get("metadata") or {}
|
|
319
|
+
if not isinstance(meta, dict):
|
|
320
|
+
meta = {}
|
|
321
|
+
result.append({
|
|
322
|
+
"id": row.get("id", ""),
|
|
323
|
+
"subject": row.get("subject", ""),
|
|
324
|
+
"status": row.get("status", "unknown"),
|
|
325
|
+
"metadata": meta,
|
|
326
|
+
"blockedBy": [],
|
|
327
|
+
})
|
|
328
|
+
return result
|
|
329
|
+
except Exception:
|
|
330
|
+
if _DAEMON_MODE == "always":
|
|
331
|
+
return []
|
|
332
|
+
return _collect_tasks_for_chain_direct(chain_id)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _collect_tasks_for_chain_direct(chain_id: str) -> list[dict]:
|
|
336
|
+
"""Direct-file fallback: scan all sessions for chain_id match."""
|
|
337
|
+
config_dir = os.environ.get(_ENV_CONFIG_DIR) or str(Path.home() / ".claude")
|
|
338
|
+
root = Path(config_dir) / "tasks"
|
|
339
|
+
if not root.exists():
|
|
340
|
+
return []
|
|
341
|
+
out = []
|
|
342
|
+
for task_file in root.rglob("*.json"):
|
|
343
|
+
try:
|
|
344
|
+
data = json.loads(task_file.read_text(encoding="utf-8"))
|
|
345
|
+
except (OSError, json.JSONDecodeError):
|
|
346
|
+
continue
|
|
347
|
+
meta = data.get("metadata") or {}
|
|
348
|
+
if meta.get("chain_id") != chain_id:
|
|
349
|
+
continue
|
|
350
|
+
out.append({
|
|
351
|
+
"id": data.get("id") or task_file.stem,
|
|
352
|
+
"subject": data.get("subject") or data.get("title") or "<untitled>",
|
|
353
|
+
"status": data.get("status") or "unknown",
|
|
354
|
+
"metadata": meta,
|
|
355
|
+
"blockedBy": data.get("blockedBy") or [],
|
|
356
|
+
})
|
|
357
|
+
return out
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def read_session_tasks(session_id: str, limit: int = 200) -> list[dict]:
|
|
361
|
+
"""Return all task dicts for a session (any status).
|
|
362
|
+
|
|
363
|
+
Used by session_fact_extractor.py and telemetry.py.
|
|
364
|
+
|
|
365
|
+
Routes via WG_DAEMON_ENABLED:
|
|
366
|
+
false → direct file scan
|
|
367
|
+
true → daemon GET /tasks?session=<id>; fallback to direct
|
|
368
|
+
always → daemon only; [] on unreachable
|
|
369
|
+
"""
|
|
370
|
+
if _DAEMON_MODE == "false":
|
|
371
|
+
return _scan_tasks_dir(session_id, limit=limit)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
rows = _daemon_get(f"/tasks?session={session_id}&limit={limit}")
|
|
375
|
+
if not rows or not isinstance(rows, list):
|
|
376
|
+
return []
|
|
377
|
+
return [
|
|
378
|
+
{
|
|
379
|
+
"id": r.get("id", ""),
|
|
380
|
+
"subject": r.get("subject", ""),
|
|
381
|
+
"status": r.get("status", "unknown"),
|
|
382
|
+
"metadata": r.get("metadata") or {},
|
|
383
|
+
}
|
|
384
|
+
for r in rows
|
|
385
|
+
]
|
|
386
|
+
except Exception:
|
|
387
|
+
if _DAEMON_MODE == "always":
|
|
388
|
+
return []
|
|
389
|
+
return _scan_tasks_dir(session_id, limit=limit)
|