nthlayer-workers 1.7.1__tar.gz → 2.0.0__tar.gz
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.
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/PKG-INFO +2 -2
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/pyproject.toml +14 -2
- nthlayer_workers-2.0.0/src/nthlayer_workers/cli_output.py +26 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/cli.py +9 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/retrospective.py +77 -24
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/worker.py +2 -2
- nthlayer_workers-2.0.0/src/nthlayer_workers/measure/adapters/prometheus.py +483 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/cli.py +22 -11
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/cli.py +9 -2
- nthlayer_workers-2.0.0/src/nthlayer_workers/observe/slo/spec_loader.py +91 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/PKG-INFO +2 -2
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/SOURCES.txt +1 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/requires.txt +1 -1
- nthlayer_workers-1.7.1/src/nthlayer_workers/measure/adapters/prometheus.py +0 -290
- nthlayer_workers-1.7.1/src/nthlayer_workers/observe/slo/spec_loader.py +0 -40
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/README.md +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/setup.cfg +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/cli.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/cli.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/config.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/changes.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/dedup.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/engine.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/temporal.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/correlation/topology.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/ingestion/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/ingestion/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/ingestion/severity.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/ingestion/webhook.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/notifications.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/prometheus.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/reasoning.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/session.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/snapshot/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/snapshot/generator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/snapshot/model.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/snapshot/token.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/state.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/store/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/store/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/store/sqlite.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/summary.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/traces/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/traces/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/traces/tempo.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/traces/topology.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/types.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/verdict_chain.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/correlate/worker.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/__main__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_apply.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_gh.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_interactive.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_interactive_app.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_preview.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_trigger.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/_yaml.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/recommendations.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/trends.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/__main__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/_parsing.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/_util.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/devin.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/gastown.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/adapters/webhook.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/calibration/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/calibration/loop.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/calibration/slos.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/calibration/verdict_calibration.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/config.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/detection/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/detection/detector.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/detection/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/governance/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/governance/engine.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/manifest.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/notifications.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/pipeline/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/pipeline/evaluator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/pipeline/router.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/store/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/store/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/store/sqlite.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/telemetry.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/tiering/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/tiering/classifier.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/tiering/promotion.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/trends/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/trends/tracker.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/types.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/measure/worker.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/__main__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/api/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/assessment.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/config.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/db/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/decision_records.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/discovery.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/backstage.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/base.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/consul.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/etcd.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/kubernetes.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/prometheus.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/dependencies/providers/zookeeper.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/deployments/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/discovery/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/discovery/classifier.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/discovery/client.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/discovery/models.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/drift/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/drift/analyzer.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/drift/models.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/drift/patterns.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/explanation.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate/conditions.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate/correlator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate/evaluator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate/policies.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/gate_adapter.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/incident.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/portfolio/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/portfolio/aggregator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/portfolio/scorer.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/slo/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/slo/collector.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/sqlite_store.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/store.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/verification/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/verification/exporter_guidance.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/verification/extractor.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/verification/models.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/verification/verifier.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/observe/worker.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/__main__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/base.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/communication.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/investigation.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/remediation.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/response_models.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/agents/triage.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/cli.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/config.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/context_store.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/coordinator.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/metrics.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notification_backends/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notification_backends/ntfy_backend.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notification_backends/protocol.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notification_backends/slack_backend.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notification_backends/stdout_backend.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/notifications.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/oncall/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/oncall/escalation.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/oncall/runner.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/oncall/schedule.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/safe_actions/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/safe_actions/actions.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/safe_actions/registry.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/safe_actions/webhook.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/server.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/__init__.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/brief.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/delegation.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/post_incident.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/shift_report.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/sre/suppression.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/types.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/verdict_submission.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/worker.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/respond/worker_helpers.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/runner.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/dependency_links.txt +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/entry_points.txt +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers.egg-info/top_level.txt +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/tests/test_cli_gate.py +0 -0
- {nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/tests/test_runner.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nthlayer-workers
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: NthLayer workers — Tier 2 background computation (observe, measure, correlate, respond, learn)
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: nthlayer-common<
|
|
7
|
+
Requires-Dist: nthlayer-common<3.0.0,>=2.1.2
|
|
8
8
|
Requires-Dist: httpx>=0.27
|
|
9
9
|
Requires-Dist: pyyaml>=6.0
|
|
10
10
|
Requires-Dist: structlog>=24.1.0
|
|
@@ -4,12 +4,24 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nthlayer-workers"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.0"
|
|
8
8
|
description = "NthLayer workers — Tier 2 background computation (observe, measure, correlate, respond, learn)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
dependencies = [
|
|
12
|
-
|
|
12
|
+
# >=2.1.2, NOT >=2.1.0 or 2.1.1. 2.1.0 introduced foreign_yaml_reason with an
|
|
13
|
+
# INERT body-recovery step: it tested spec.slos as a list, but v1 writes
|
|
14
|
+
# a mapping and v2 has no spec.slos at all, so a header-stripped manifest
|
|
15
|
+
# was dropped with parse_failures == 0 — the exact silent drop this
|
|
16
|
+
# package uses the gate to prevent. Fixed across 2.1.1 and 2.1.2;
|
|
17
|
+
# 2.1.1 still counted every Backstage catalog-info.yaml as a broken
|
|
18
|
+
# manifest, which 2.1.2 corrected.
|
|
19
|
+
#
|
|
20
|
+
# CI resolves nthlayer-common from a sibling checkout, so it would not
|
|
21
|
+
# notice a bad resolve. tests/observe/test_slo_spec_loader.py's
|
|
22
|
+
# TestSharedGateResolvesCorrectly exists for that: it uses the shapes the
|
|
23
|
+
# PARSERS produce and fails against 2.1.0.
|
|
24
|
+
"nthlayer-common>=2.1.2,<3.0.0",
|
|
13
25
|
"httpx>=0.27",
|
|
14
26
|
"pyyaml>=6.0",
|
|
15
27
|
"structlog>=24.1.0",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Operator-facing CLI output shared across worker modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def warn_parse_failures(parse_failures: int, specs_dir: str | Path) -> None:
|
|
10
|
+
"""Tell the operator when the SLOs that follow cover only part of a directory.
|
|
11
|
+
|
|
12
|
+
Surfaced on stderr rather than only logged: whoever reads SLO output is
|
|
13
|
+
the one who needs to know the view is partial, and a warning in a log
|
|
14
|
+
they are not tailing does not reach them (opensrm-3470).
|
|
15
|
+
|
|
16
|
+
Takes the count, not a ``LoadedSpecs`` — observe and measure each have
|
|
17
|
+
their own, carrying differently-shaped SLOs under differently-named
|
|
18
|
+
fields (``service_slos`` vs ``slos``). The only thing they share is this
|
|
19
|
+
number, so that is the whole parameter (opensrm-fxln).
|
|
20
|
+
"""
|
|
21
|
+
if parse_failures:
|
|
22
|
+
print(
|
|
23
|
+
f"Warning: {parse_failures} manifest(s) in {specs_dir} "
|
|
24
|
+
f"failed to parse and were not evaluated",
|
|
25
|
+
file=sys.stderr,
|
|
26
|
+
)
|
|
@@ -139,6 +139,15 @@ def _cmd_retrospective(args: argparse.Namespace) -> None:
|
|
|
139
139
|
impact = custom.get("financial_impact")
|
|
140
140
|
if impact:
|
|
141
141
|
print(f" Financial impact: ${impact.get('estimated', 0):.2f} ({impact.get('failure_mode')})")
|
|
142
|
+
# opensrm-oh27: an unparseable manifest drops its service from the
|
|
143
|
+
# figures above. Print the count so the reader knows the numbers
|
|
144
|
+
# were computed over a subset rather than over everything.
|
|
145
|
+
parse_failures = custom.get("manifest_parse_failures", 0)
|
|
146
|
+
if parse_failures:
|
|
147
|
+
print(
|
|
148
|
+
f" Manifest parse failures: {parse_failures} "
|
|
149
|
+
"(figures computed over a subset; see manifest_parse_failed logs)"
|
|
150
|
+
)
|
|
142
151
|
except KeyError as e:
|
|
143
152
|
print(f"Error: {e}", file=__import__("sys").stderr)
|
|
144
153
|
__import__("sys").exit(1)
|
{nthlayer_workers-1.7.1 → nthlayer_workers-2.0.0}/src/nthlayer_workers/learn/retrospective.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Retrospective builder — walks verdict lineage to produce a post-incident analysis."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
from dataclasses import dataclass
|
|
4
5
|
from datetime import UTC, datetime, timedelta
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
@@ -9,6 +10,8 @@ import structlog
|
|
|
9
10
|
from nthlayer_common.manifest import (
|
|
10
11
|
ManifestLoadError,
|
|
11
12
|
extract_declared_dependencies,
|
|
13
|
+
foreign_yaml_reason,
|
|
14
|
+
iter_manifest_files,
|
|
12
15
|
load_manifest,
|
|
13
16
|
)
|
|
14
17
|
from nthlayer_common.outcomes import (
|
|
@@ -116,8 +119,8 @@ def build_retrospective(
|
|
|
116
119
|
# ground-truth view of operator-declared deps that downstream
|
|
117
120
|
# add_dependency recommendation logic compares against the
|
|
118
121
|
# incident's observed call graph.
|
|
119
|
-
|
|
120
|
-
declared_dependencies_by_service = _extract_declared_dependencies(
|
|
122
|
+
loaded_specs = _load_manifests_from_specs(specs_dir)
|
|
123
|
+
declared_dependencies_by_service = _extract_declared_dependencies(loaded_specs.manifests)
|
|
121
124
|
|
|
122
125
|
# Financial impact (if specs available)
|
|
123
126
|
financial_impact = _compute_financial_impact(
|
|
@@ -125,7 +128,7 @@ def build_retrospective(
|
|
|
125
128
|
duration_minutes,
|
|
126
129
|
breach_counts_by_service,
|
|
127
130
|
specs_dir,
|
|
128
|
-
|
|
131
|
+
manifests_by_service=loaded_specs.manifests,
|
|
129
132
|
)
|
|
130
133
|
|
|
131
134
|
# Generate recommendations
|
|
@@ -155,6 +158,10 @@ def build_retrospective(
|
|
|
155
158
|
"financial_impact": financial_impact,
|
|
156
159
|
"recommendations": recommendations,
|
|
157
160
|
"declared_dependencies_by_service": declared_dependencies_by_service,
|
|
161
|
+
# opensrm-oh27: always present, 0 on a clean load. An absent key
|
|
162
|
+
# would read the same as "no failures", which is the ambiguity
|
|
163
|
+
# the silent skip created in the first place.
|
|
164
|
+
"manifest_parse_failures": loaded_specs.parse_failures,
|
|
158
165
|
}
|
|
159
166
|
if trigger_service is not None:
|
|
160
167
|
retro_custom["trigger_service"] = trigger_service
|
|
@@ -225,32 +232,74 @@ def _parse_ts(ts: Any) -> datetime:
|
|
|
225
232
|
return dt
|
|
226
233
|
|
|
227
234
|
|
|
228
|
-
|
|
235
|
+
@dataclass(frozen=True)
|
|
236
|
+
class LoadedManifests:
|
|
237
|
+
"""Outcome of scanning a specs directory (opensrm-oh27).
|
|
238
|
+
|
|
239
|
+
``manifests`` is the parsed view keyed by service name; ``parse_failures``
|
|
240
|
+
counts *files* that raised ``ManifestLoadError``, not services missing
|
|
241
|
+
from ``manifests`` — a failed environment overlay for a service whose
|
|
242
|
+
base manifest parsed still counts one. The count is deliberately a
|
|
243
|
+
coverage-doubt signal rather than a precise miss-count, and it travels
|
|
244
|
+
with the manifests because the downstream financial figure is computed
|
|
245
|
+
over ``manifests`` alone: without it, a partial view is
|
|
246
|
+
indistinguishable from a complete one. The per-file
|
|
247
|
+
``manifest_parse_failed`` warning carries which file and why.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
manifests: dict[str, Any]
|
|
251
|
+
parse_failures: int = 0
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _load_manifests_from_specs(specs_dir: str | None) -> LoadedManifests:
|
|
229
255
|
"""Load all ``*.yaml`` manifests under ``specs_dir`` once, keyed by
|
|
230
256
|
``manifest.name`` (the canonical service identity).
|
|
231
257
|
|
|
232
|
-
Returns
|
|
258
|
+
Returns empty manifests when ``specs_dir`` is None or not a directory,
|
|
233
259
|
so callers can treat the result uniformly without re-checking the
|
|
234
260
|
filesystem. Duplicate manifests for the same service (template forks,
|
|
235
261
|
environment overlays) are deduplicated by keeping the first one
|
|
236
262
|
encountered and emitting a warning — this matches the pre-jmy.21
|
|
237
263
|
behaviour of ``_compute_financial_impact``.
|
|
238
264
|
|
|
239
|
-
Parse failures from ``load_manifest`` are
|
|
240
|
-
|
|
241
|
-
|
|
265
|
+
Parse failures from ``load_manifest`` are logged and counted, then
|
|
266
|
+
skipped (opensrm-oh27). ``UnicodeDecodeError`` is caught alongside
|
|
267
|
+
``ManifestLoadError`` because ``load_manifest`` opens with the default
|
|
268
|
+
encoding and only wraps ``YAMLError`` — non-UTF-8 bytes would otherwise
|
|
269
|
+
abort the whole retrospective, a hard stop where the design says log and
|
|
270
|
+
continue. The financial-impact and dependency-extraction
|
|
271
|
+
paths both tolerate a partial view, but the caller is told how partial
|
|
272
|
+
it is via ``LoadedManifests.parse_failures``.
|
|
242
273
|
"""
|
|
243
274
|
if not specs_dir:
|
|
244
|
-
return {}
|
|
275
|
+
return LoadedManifests(manifests={})
|
|
245
276
|
specs_path = Path(specs_dir)
|
|
246
277
|
if not specs_path.is_dir():
|
|
247
|
-
return {}
|
|
278
|
+
return LoadedManifests(manifests={})
|
|
248
279
|
|
|
249
280
|
manifests: dict[str, Any] = {}
|
|
250
|
-
|
|
281
|
+
parse_failures = 0
|
|
282
|
+
for spec_file in iter_manifest_files(specs_path):
|
|
251
283
|
try:
|
|
252
284
|
manifest = load_manifest(str(spec_file))
|
|
253
|
-
except ManifestLoadError:
|
|
285
|
+
except (ManifestLoadError, UnicodeDecodeError) as exc:
|
|
286
|
+
foreign_reason = foreign_yaml_reason(spec_file)
|
|
287
|
+
if foreign_reason is not None:
|
|
288
|
+
# Foreign YAML sharing the directory, not a broken manifest.
|
|
289
|
+
# Recorded rather than dropped without trace — skipping
|
|
290
|
+
# without saying so is the shape this bead exists to remove.
|
|
291
|
+
log.debug(
|
|
292
|
+
"manifest_file_ignored",
|
|
293
|
+
spec_file=str(spec_file),
|
|
294
|
+
reason=foreign_reason,
|
|
295
|
+
)
|
|
296
|
+
continue
|
|
297
|
+
parse_failures += 1
|
|
298
|
+
log.warning(
|
|
299
|
+
"manifest_parse_failed",
|
|
300
|
+
spec_file=str(spec_file),
|
|
301
|
+
error=str(exc),
|
|
302
|
+
)
|
|
254
303
|
continue
|
|
255
304
|
if manifest.name in manifests:
|
|
256
305
|
log.warning(
|
|
@@ -260,16 +309,16 @@ def _load_manifests_from_specs(specs_dir: str | None) -> dict[str, Any]:
|
|
|
260
309
|
)
|
|
261
310
|
continue
|
|
262
311
|
manifests[manifest.name] = manifest
|
|
263
|
-
return manifests
|
|
312
|
+
return LoadedManifests(manifests=manifests, parse_failures=parse_failures)
|
|
264
313
|
|
|
265
314
|
|
|
266
315
|
def _extract_declared_dependencies(
|
|
267
|
-
|
|
316
|
+
manifests_by_service: dict[str, Any],
|
|
268
317
|
) -> dict[str, list[str]]:
|
|
269
318
|
"""Wrapper around the shared helper (opensrm-dpws). The CLI path
|
|
270
319
|
works with Manifest dataclass instances loaded via load_manifest().
|
|
271
320
|
"""
|
|
272
|
-
return extract_declared_dependencies(from_manifests=
|
|
321
|
+
return extract_declared_dependencies(from_manifests=manifests_by_service)
|
|
273
322
|
|
|
274
323
|
|
|
275
324
|
def _compute_financial_impact(
|
|
@@ -278,7 +327,7 @@ def _compute_financial_impact(
|
|
|
278
327
|
breach_counts_by_service: dict[str, int],
|
|
279
328
|
specs_dir: str | None,
|
|
280
329
|
*,
|
|
281
|
-
|
|
330
|
+
manifests_by_service: dict[str, Any] | None = None,
|
|
282
331
|
) -> dict | None:
|
|
283
332
|
"""Compute spec § 1 financial impact across blast-radius services.
|
|
284
333
|
|
|
@@ -289,10 +338,14 @@ def _compute_financial_impact(
|
|
|
289
338
|
services that share a currency; mixed-currency manifests are
|
|
290
339
|
skipped with a warning.
|
|
291
340
|
|
|
292
|
-
``
|
|
293
|
-
``_load_manifests_from_specs`` (opensrm-jmy.21). When
|
|
294
|
-
function loads manifests itself — preserves the pre-jmy.21
|
|
295
|
-
convention used by ``tests/learn/test_retrospective_financial.py``.
|
|
341
|
+
``manifests_by_service`` is the shared cache produced by
|
|
342
|
+
``_load_manifests_from_specs(...).manifests`` (opensrm-jmy.21). When
|
|
343
|
+
omitted the function loads manifests itself — preserves the pre-jmy.21
|
|
344
|
+
calling convention used by ``tests/learn/test_retrospective_financial.py``.
|
|
345
|
+
That self-loading path discards the parse-failure count; only
|
|
346
|
+
``build_retrospective`` surfaces it (as
|
|
347
|
+
``metadata.custom["manifest_parse_failures"]``), and the per-file
|
|
348
|
+
warning log fires either way.
|
|
296
349
|
"""
|
|
297
350
|
if not specs_dir or not blast_radius:
|
|
298
351
|
return None
|
|
@@ -307,9 +360,9 @@ def _compute_financial_impact(
|
|
|
307
360
|
)
|
|
308
361
|
return None
|
|
309
362
|
|
|
310
|
-
if
|
|
311
|
-
|
|
312
|
-
if not
|
|
363
|
+
if manifests_by_service is None:
|
|
364
|
+
manifests_by_service = _load_manifests_from_specs(specs_dir).manifests
|
|
365
|
+
if not manifests_by_service:
|
|
313
366
|
return None
|
|
314
367
|
|
|
315
368
|
affected_services = {
|
|
@@ -322,7 +375,7 @@ def _compute_financial_impact(
|
|
|
322
375
|
currency: str | None = None
|
|
323
376
|
aggregate_source: str | None = None
|
|
324
377
|
|
|
325
|
-
for service_name, manifest in
|
|
378
|
+
for service_name, manifest in manifests_by_service.items():
|
|
326
379
|
if service_name not in affected_services:
|
|
327
380
|
continue
|
|
328
381
|
if manifest.outcomes is None:
|
|
@@ -323,8 +323,8 @@ class LearnRetrospectiveModule:
|
|
|
323
323
|
]
|
|
324
324
|
if len(trigger_matches) > 1:
|
|
325
325
|
# First-wins (matches the existing
|
|
326
|
-
# _load_manifests_from_specs dedup behaviour
|
|
327
|
-
#
|
|
326
|
+
# _load_manifests_from_specs dedup behaviour on the
|
|
327
|
+
# CLI side, which logs manifest_duplicate_skipped).
|
|
328
328
|
logger.warning(
|
|
329
329
|
"learn_manifest_duplicate_skipped",
|
|
330
330
|
service=trigger_service,
|