org-knowledge-layer 0.1.0__py3-none-any.whl
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.
- okl/__init__.py +12 -0
- okl/__main__.py +8 -0
- okl/bootstrap.py +83 -0
- okl/cli.py +484 -0
- okl/client.py +160 -0
- okl/core.py +223 -0
- okl/drift.py +119 -0
- okl/mcp_server.py +75 -0
- okl/scaffold/MANIFEST.md +59 -0
- okl/scaffold/ci/method-gates.yml +32 -0
- okl/scaffold/ci/okl-verify.yml +59 -0
- okl/scaffold/claude/agents/architecture-reviewer.md +41 -0
- okl/scaffold/claude/commands/check-rules.md +24 -0
- okl/scaffold/claude/commands/feature-spec.md +37 -0
- okl/scaffold/claude/rules/example-area.md +22 -0
- okl/scaffold/claude/skills/RECOMMENDED-COMPANIONS.md +40 -0
- okl/scaffold/claude/skills/encoding-loop/SKILL.md +48 -0
- okl/scaffold/claude/skills/verify-before-claiming/SKILL.md +56 -0
- okl/scaffold/evals/README.md +32 -0
- okl/scaffold/evals/cases.jsonl +1 -0
- okl/scaffold/evals/run_evals.py +109 -0
- okl/scaffold/gates/check-canon-size.sh +11 -0
- okl/scaffold/gates/check-doc-orphans.sh +19 -0
- okl/scaffold/gates/check-retractions.sh +22 -0
- okl/scaffold/gates/check-tombstones.sh +22 -0
- okl/scaffold/gates/run-gates.sh +31 -0
- okl/scaffold/hooks/hooks.json +16 -0
- okl/scaffold/hooks/stop-okl-encode.sh +78 -0
- okl/scaffold/hooks/userpromptsubmit-okl-check.sh +68 -0
- okl/scaffold/plugin/plugin.json +10 -0
- okl/scaffold/profiles/dotnet/README.md +12 -0
- okl/scaffold/profiles/dotnet/rules/architecture.md +55 -0
- okl/scaffold/profiles/dotnet/rules/messaging.md +31 -0
- okl/scaffold/profiles/dotnet/rules/performance-and-data.md +36 -0
- okl/scaffold/profiles/dotnet/rules/security.md +42 -0
- okl/scaffold/profiles/geospatial/README.md +6 -0
- okl/scaffold/profiles/geospatial/rules/geospatial-ml.md +38 -0
- okl/scaffold/profiles/python-rag/README.md +13 -0
- okl/scaffold/profiles/python-rag/rules/fastapi-backend.md +37 -0
- okl/scaffold/profiles/python-rag/rules/project-structure.md +28 -0
- okl/scaffold/profiles/python-rag/rules/rag-pipeline.md +73 -0
- okl/scaffold/profiles/react/README.md +18 -0
- okl/scaffold/profiles/react/rules/frontend.md +57 -0
- okl/scaffold/registries/RETRACTIONS.md +19 -0
- okl/scaffold/registries/tombstones.txt +7 -0
- okl/scaffold/root/CLAUDE.md +55 -0
- okl/scaffold/root/METHOD.md +64 -0
- okl/scaffold_cmd.py +110 -0
- okl/seed/dotnet-canon.json +489 -0
- okl/seed/dotnet-decisions.json +328 -0
- okl/seed/dotnet-defects.json +133 -0
- okl/seed/dotnet-review-surfaces.json +147 -0
- okl/seed/frontend-canon.json +116 -0
- okl/seed/geospatial-deeptime-defects.json +59 -0
- okl/seed/geospatial-defects.json +154 -0
- okl/seed/geospatial-enforcement-defects.json +121 -0
- okl/seed/geospatial-eval-defects.json +25 -0
- okl/seed/rag-defects.json +120 -0
- okl/seed/react-defects.json +45 -0
- okl/seed.py +55 -0
- okl/service.py +137 -0
- okl/store.py +432 -0
- org_knowledge_layer-0.1.0.dist-info/METADATA +475 -0
- org_knowledge_layer-0.1.0.dist-info/RECORD +67 -0
- org_knowledge_layer-0.1.0.dist-info/WHEEL +4 -0
- org_knowledge_layer-0.1.0.dist-info/entry_points.txt +2 -0
- org_knowledge_layer-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "the .NET platform REVIEW-SURFACES import — rules encoded in .claude/agents/architecture-reviewer.md (pattern checklist), .coderabbit.yaml path_instructions, and .claude/audits/ verdicts, deduped against dotnet-canon.json and dotnet-decisions.json. These are the rules the repo's automated reviewers enforce; porting them means another repo's agent learns them at check time instead of at review time. Review, then `okl seed seed/dotnet-review-surfaces.json`.",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"key": "rs_endpoint_group_hygiene",
|
|
6
|
+
"type": "Rule", "scope": "repo", "repo": "dotnet-microservices", "verified": true,
|
|
7
|
+
"found_by": "architecture-reviewer.md pattern checklist; .coderabbit.yaml **/Endpoints/**/*.cs",
|
|
8
|
+
"tags": "dotnet,security",
|
|
9
|
+
"title": "Endpoints register via the shared versioned-group helper, with group-level auth and server-side paging clamps",
|
|
10
|
+
"body": "Every HTTP endpoint registers via MapV1ApiGroup(tag, name) instead of hand-rolled versioning chains, non-public groups get RequireAuthorization at the group level, and every list endpoint accepts (page, pageSize) clamped server-side at ≤100. Centralizing the policy in one helper makes per-service drift structurally impossible.",
|
|
11
|
+
"symptom": "a new endpoint hand-rolls versioning, is anonymously reachable, or returns an uncapped list",
|
|
12
|
+
"fix": "register on the versioned group helper, require auth at group level, clamp pageSize server-side"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"key": "rs_read_projection_axes",
|
|
16
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
17
|
+
"found_by": "architecture-reviewer.md; docs/cqrs-data-access.md; audits/2026-05-31-ef-core-read-performance",
|
|
18
|
+
"tags": "dotnet,data-quality",
|
|
19
|
+
"title": "Cartesian explosion has two axes — only projection-to-DTO fixes both; plain AsNoTracking is a half-fix",
|
|
20
|
+
"body": "Include-based reads suffer client-side object duplication (fixed by identity-resolution tracking variants) AND cartesian SQL row shape (fixed only by projection or split queries). AsNoTracking returning an entity fixes neither the wire shape nor duplication. Inline projection to a DTO with nested collections wins on both axes at once — which is why it's the default read shape; identity-resolution no-tracking is the narrow fallback for materializing an untracked entity graph.",
|
|
21
|
+
"symptom": "a read path materializing entities with Include, duplicating parents per child row",
|
|
22
|
+
"fix": "project inline to a DTO with nested collections; the ORM auto-splits projected collection navigations"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "rs_fanout_per_recipient",
|
|
26
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
27
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml **/Features/**/*.cs",
|
|
28
|
+
"tags": "messaging",
|
|
29
|
+
"title": "Broadcast-to-N is one message per recipient under a bounded-concurrency queue — never a foreach-await loop",
|
|
30
|
+
"body": "A handler iterating recipients and awaiting a sender per recipient holds the request open for N × latency, concentrates work on one process, and spikes the rest of the system. Publish one message per recipient (or batch of K), return immediately, and process under an explicit parallelism cap — the throttle gives natural back-pressure so fast producers can't starve slow consumers.",
|
|
31
|
+
"symptom": "foreach (recipient) await sender.Send(...) inside a request or message handler",
|
|
32
|
+
"fix": "per-recipient messages processed under a bounded-concurrency local queue"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"key": "rs_meter_wildcard_registration",
|
|
36
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
37
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml ServiceDefaults/Extensions.cs",
|
|
38
|
+
"tags": "dotnet,messaging,data-quality",
|
|
39
|
+
"title": "Frameworks that suffix meter names need wildcard registration — the literal name silently collects nothing",
|
|
40
|
+
"body": "Wolverine names its meter 'Wolverine:{ServiceName}', so telemetry must register AddMeter(\"Wolverine*\"); a literal AddMeter(\"Wolverine\") collects zero metrics with no error. Flag any 'tidying' of the wildcard to a literal. Corollary: prefer the framework's own instruments (DLQ depth, execution failures, sent/received, inbox/outbox depth) over hand-rolled equivalents — see the dead-metric lesson.",
|
|
41
|
+
"symptom": "messaging metrics vanish from the dashboard after an innocuous meter-name cleanup",
|
|
42
|
+
"fix": "keep the wildcard meter registration; lean on framework-emitted instruments"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "rs_sweeper_hygiene",
|
|
46
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
47
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml **/*RecoveryJob*.cs",
|
|
48
|
+
"tags": "dotnet,method",
|
|
49
|
+
"title": "Background sweepers: fresh DI scope per iteration, no-wait distributed lock, injected clock, per-row exception isolation",
|
|
50
|
+
"body": "Cron-style background jobs create a fresh DI scope per iteration (a reused scope accumulates every row in the change tracker), take a no-wait distributed lock when running cross-replica (released in await-using for exception safety), inject TimeProvider instead of DateTime.UtcNow for test determinism, and wrap each iteration in try/catch so one poison row can't kill the sweep.",
|
|
51
|
+
"symptom": "sweep memory grows with table size, duplicate work across replicas, or one bad row aborts the run",
|
|
52
|
+
"fix": "scope-per-iteration, no-wait lock, injected clock, per-iteration try/catch"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"key": "rs_rich_aggregate_shape",
|
|
56
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
57
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml domain-invariant path rules",
|
|
58
|
+
"tags": "dotnet",
|
|
59
|
+
"title": "Aggregate shape (when earned): validating factory, private setters, named transitions, read-only collections, zero dependencies",
|
|
60
|
+
"body": "Entities are built via a validating static Create factory with private setters; state changes go through named transition methods (MarkAsPaid(), never Status = Paid) that throw on invalid transitions; collections expose IReadOnlyList over a private list, mutated only via named methods. The domain references nothing — no ORM, logging, or messaging; concurrency tokens live as shadow state in the persistence layer so the entity stays clean. (When the shape is earned at all — see the observed-invariant rule.)",
|
|
61
|
+
"symptom": "public setters, direct property mutation from handlers, mutable exposed lists, or infra references in Domain",
|
|
62
|
+
"fix": "factory + named transitions + read-only collections; shadow concurrency tokens"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"key": "rs_handler_idempotency_guard",
|
|
66
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
67
|
+
"found_by": "architecture-reviewer.md; audits/2026-05-31-idempotent-consumer",
|
|
68
|
+
"tags": "messaging",
|
|
69
|
+
"title": "Idempotency lives at the handler (pre-check and no-op); the aggregate's throw is the backstop, not the guard",
|
|
70
|
+
"body": "Consume handlers pre-check state before invoking the transition (if status isn't the expected precursor, return) so benign redeliveries no-op. The aggregate's throw-on-invalid-transition remains as the invariant backstop — but if the throw IS the guard, every duplicate or late delivery lands on the DLQ as a false poison message. Tests for these guards must state that a no-op, not a throw, is the intended behavior.",
|
|
71
|
+
"symptom": "duplicate/late-delivered messages filling the DLQ via aggregate invariant throws",
|
|
72
|
+
"fix": "status short-circuit at the top of the consume handler; aggregate throw as defense in depth"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"key": "rs_keyed_services_over_factories",
|
|
76
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
77
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml **/Infrastructure/DependencyInjection.cs",
|
|
78
|
+
"tags": "dotnet",
|
|
79
|
+
"title": "Multi-impl ports route via keyed DI; premature factories and unrouted multi-registrations are twin failure modes",
|
|
80
|
+
"body": "Two DI-registration bugs: (1) premature factory — a keyed/factory setup wrapping a port with exactly one implementation is speculative coupling; plain scoped registration is right until a second impl ships. (2) missing routing — with 2+ impls registered plain AND per-call selection intended (a Channel field picking the sender), DI silently returns the last-registered impl for every call, dropping the routing intent: a latent bug, not a style nit. Use the container's keyed-services API for routing — never a hand-rolled port factory, since the container IS the canonical factory.",
|
|
81
|
+
"symptom": "every call routes to one impl regardless of the selector field, or an unused factory wraps a single impl",
|
|
82
|
+
"fix": "one impl: plain registration. multiple + per-call selection: keyed services keyed by the routing value"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"key": "rs_integration_tests_default",
|
|
86
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
87
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml **/*Test*.cs",
|
|
88
|
+
"tags": "dotnet,method",
|
|
89
|
+
"title": "Integration tests are the default tier for handlers; unit tests are reserved for pure domain logic — and every guard branch gets a test",
|
|
90
|
+
"body": "Any handler touching the DbContext is tested through the real API against real containers: hit the endpoint, assert DB state and tracked message envelopes — not mock-a-repository-and-count-calls. Unit tests cover pure logic only (transition guards, validation). A mock of a repository wrapper in a unit test signals two defects at once: the wrapper shouldn't exist and the test belongs in the integration tier. A handler with security guards, idempotency short-circuits, or ordering invariants needs a test per branch — one happy-path test on a three-branch handler is a finding.",
|
|
91
|
+
"symptom": "handler tests mocking repositories and verifying call counts, or multi-branch handlers with one happy-path test",
|
|
92
|
+
"fix": "move DbContext-touching tests to the integration tier; enumerate the missing branch scenarios"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"key": "rs_workflow_hardening_baseline",
|
|
96
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
97
|
+
"found_by": "architecture-reviewer.md; .coderabbit.yaml .github/workflows/*.yml",
|
|
98
|
+
"tags": "security,method",
|
|
99
|
+
"title": "CI workflow baseline: pipefail, no persisted credentials, least-privilege permissions, concurrency groups — pin actions in one batch, not piecemeal",
|
|
100
|
+
"body": "Every bash run block starts with set -euo pipefail; checkout sets persist-credentials false when the job doesn't push; every workflow declares explicit least-privilege permissions and a concurrency group cancelling superseded runs. Deliberately NOT a per-PR finding: individually unpinned @vN actions — SHA-pinning one workflow while others float is inconsistency theater, so pinning is a single repo-wide hardening pass.",
|
|
101
|
+
"symptom": "a run block continuing past a failed pipe segment, or a read-only job persisting its checkout token",
|
|
102
|
+
"fix": "apply the four baseline hardenings on every new workflow; batch SHA-pinning as one dedicated PR"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"key": "rs_event_carries_recipient_id",
|
|
106
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
107
|
+
"found_by": ".coderabbit.yaml App.Contracts/**/*.cs (precedent: issue #99, ShipmentDispatchedEvent)",
|
|
108
|
+
"tags": "messaging,data-quality",
|
|
109
|
+
"title": "Events that can trigger notifications must denormalize the recipient ID — an OrderId can never resolve to an inbox",
|
|
110
|
+
"body": "Any cross-service event that triggers (or could trigger) a user-facing notification downstream carries the recipient identifier denormalized from the producing aggregate; a consumer keying a notification on an entity ID has a recipient-key-that-can't-resolve bug. Precedent: a dispatch event shipped without BuyerId while sibling events had it, silently breaking 'Order Shipped' emails. Contract changes are additive-only within a major version — add init-only properties, never remove or rename fields consumers bind to.",
|
|
111
|
+
"symptom": "a notification consumer silently dropping messages because the event lacks a resolvable recipient",
|
|
112
|
+
"fix": "add the denormalized recipient field additively; audit consumers for non-recipient keys"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"key": "rs_transport_removal_orphans",
|
|
116
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
117
|
+
"found_by": ".coderabbit.yaml ServiceDefaults/Extensions.cs (both bit the ASB→RabbitMQ swap, PR #159)",
|
|
118
|
+
"tags": "dotnet,messaging",
|
|
119
|
+
"title": "Removing a transport package leaves two silent orphans: dead telemetry source registrations and stranded central package versions",
|
|
120
|
+
"body": "Deleting the PackageReference is not enough. Sweep for (1) a dead AddSource(...) for the removed transport's ActivitySource in the tracing block — it silently collects nothing and masks that the replacement's source may be missing — and (2) an orphaned PackageVersion in the central versions file with no remaining reference. Both bit the ASB-to-RabbitMQ swap.",
|
|
121
|
+
"symptom": "messaging spans missing after a transport swap; stale central package versions",
|
|
122
|
+
"fix": "pair every transport removal with a tracing-source and central-package sweep in the same PR"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"key": "au_di_lifetimes_captive_dependency",
|
|
126
|
+
"type": "Claim", "scope": "org", "repo": "dotnet-microservices", "verified": true, "status": "live",
|
|
127
|
+
"found_by": "audits/2026-06-03-di-lifetimes + 2026-06-03-iservicescope-vs-iserviceprovider (gap tracked as #103)",
|
|
128
|
+
"tags": "dotnet",
|
|
129
|
+
"title": "Audit verdict: captive-dependency rules held by habit only — enforce with ValidateScopes/ValidateOnBuild, not tribal knowledge",
|
|
130
|
+
"body": "The DI-lifetimes audit found the project avoids captive dependencies (a singleton capturing a scoped service) purely by developer behavior — no ValidateScopes/ValidateOnBuild enforcement, no canon rule. The follow-up audit added the adjacent anti-pattern: resolving scoped services from the root provider instead of minting a scope. Lesson: lifetime discipline should be build-time-validated, not tribal.",
|
|
131
|
+
"symptom": "a DI container with scope-validation off and no lifetime rules in canon",
|
|
132
|
+
"fix": "enable ValidateScopes+ValidateOnBuild; encode the captive-dependency and root-provider rules"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"key": "au_audit_examples_not_advice",
|
|
136
|
+
"type": "Claim", "scope": "org", "repo": "dotnet-microservices", "verified": true, "status": "live",
|
|
137
|
+
"found_by": "audits/2026-06-12-dotnet-microservices-do-avoid (Kerim Kara guide)",
|
|
138
|
+
"tags": "method,eval-integrity",
|
|
139
|
+
"title": "Audit verdict: audit an article's EXAMPLES, not just its stated advice — this one violated its own canon three times",
|
|
140
|
+
"body": "An audit of a do/avoid microservices guide found all 10 stated items already covered by canon — but the article's own code examples contradicted it three times: publish-after-save dual-write instead of an outbox, audience validation disabled against the JWT rule, and a service-interface layer against the architecture. The audit also surfaced a recurring implicit stance the project practiced but never documented (internal-gRPC trust, no API gateway) — the second undocumented-stance find that day. Method lesson: examples are where an article's real teaching lives, and where it quietly contradicts itself."
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"edges": [
|
|
144
|
+
{"src": "rs_meter_wildcard_registration", "rel": "ENCODES", "dst": "seed:dotnet-defects:nc_dead_metric"},
|
|
145
|
+
{"src": "rs_keyed_services_over_factories", "rel": "ENCODES", "dst": "seed:dotnet-defects:nc_speculative_interface"}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "the .NET platform FRONTEND canon import (frontend/CLAUDE.md, 122-line React canon; complements the 3 nodes in react-defects.json: useEffect-fetch ban, localStorage tokens, React Compiler). React is backend-agnostic so most rules are org-scoped with tag react. Not *-defects.json on purpose — review, then `okl seed seed/frontend-canon.json`.",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"key": "rxc_feature_boundaries",
|
|
6
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
7
|
+
"found_by": "frontend/CLAUDE.md 'Architecture rules'",
|
|
8
|
+
"tags": "react",
|
|
9
|
+
"title": "Feature boundaries are enforced, not conventional — cross-feature imports only via the public index, as a build error",
|
|
10
|
+
"body": "Feature folders own their components/hooks/api/types; other features import only the feature's index.ts public API, and shared/ never imports from features. The boundary is made real by lint (import/no-restricted-paths) failing the build, not by convention. Barrels are intentional at feature boundaries only — wildcard re-export barrels elsewhere defeat tree-shaking and bloat chunks.",
|
|
11
|
+
"symptom": "an import reaching into another feature's internal file path",
|
|
12
|
+
"fix": "import via the feature's index.ts; enforce with import/no-restricted-paths as an error"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"key": "rxc_shared_promotion",
|
|
16
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
17
|
+
"found_by": "frontend/CLAUDE.md 'Architecture rules'",
|
|
18
|
+
"tags": "react,method",
|
|
19
|
+
"title": "Promotion to shared/ requires proven reuse (2-3 features), not anticipation",
|
|
20
|
+
"body": "Speculative abstraction in a shared folder is the same dead weight as speculative interfaces in the backend: it adds surface nobody substitutes. A helper moves from a feature to shared/ only when two-to-three features independently need it today.",
|
|
21
|
+
"symptom": "a utility placed in shared/ with a single consumer",
|
|
22
|
+
"fix": "keep it in the owning feature until reuse is demonstrated"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "rxc_query_keys",
|
|
26
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
27
|
+
"found_by": "frontend/CLAUDE.md 'Server state'",
|
|
28
|
+
"tags": "react",
|
|
29
|
+
"title": "Query keys are a typed convention — [feature, entity, params] via key factories, not ad-hoc strings",
|
|
30
|
+
"body": "Cache identity is the contract of a query library; free-form keys make invalidation guesswork. Keys follow [feature, entity, params] and are produced by key factories living in the feature's api module, so invalidation targets are constructible anywhere the mutation lives.",
|
|
31
|
+
"symptom": "hand-written query key arrays/strings scattered across components",
|
|
32
|
+
"fix": "define per-feature key factories and use them for both queries and invalidation"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"key": "rxc_mutation_invalidate",
|
|
36
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
37
|
+
"found_by": "frontend/CLAUDE.md 'Server state'",
|
|
38
|
+
"tags": "react",
|
|
39
|
+
"title": "Mutations invalidate (or update) their affected queries in the mutation definition — never 'later' or via refetch luck",
|
|
40
|
+
"body": "The backend's cache-invalidation-in-the-write-path rule, client-side: the onSuccess of the mutation is the write path, so the cache update belongs there, co-located and guaranteed. Relying on refetch-on-focus or a later effect leaves windows where the UI shows stale server state.",
|
|
41
|
+
"symptom": "a mutation whose affected queries are refreshed somewhere other than the mutation definition",
|
|
42
|
+
"fix": "invalidate or setQueryData in the mutation's onSuccess, using the feature's key factory"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "rxc_no_state_copy",
|
|
46
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
47
|
+
"found_by": "frontend/CLAUDE.md 'Server state'",
|
|
48
|
+
"tags": "react",
|
|
49
|
+
"title": "Server data is not copied into useState — render from the query result and derive during render",
|
|
50
|
+
"body": "Copying a query result into local state creates a second source of truth that silently goes stale after the next refetch or mutation. Derive view data during render (memoize only if measured-expensive); local state is for genuinely local UI concerns.",
|
|
51
|
+
"symptom": "useState initialized from (or synced with) a query result",
|
|
52
|
+
"fix": "render directly from the query result; derive during render"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"key": "rxc_effects_discipline",
|
|
56
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
57
|
+
"found_by": "frontend/CLAUDE.md 'Effects discipline' (distilled from react.dev You Might Not Need an Effect)",
|
|
58
|
+
"tags": "react",
|
|
59
|
+
"title": "An Effect is only for synchronizing with an external system — and dependency arrays are facts, not knobs",
|
|
60
|
+
"body": "Derived data is calculated during render; state reset on prop change uses the key prop; 'user did X' logic (POST, navigation, notifications) lives in the event handler, never an effect watching a flag; external stores use useSyncExternalStore; effect-sets-state-triggers-effect chains collapse into the handler. Never lie to the dependency array to control WHEN an effect runs — restructure instead; exhaustive-deps is an error, not a warning.",
|
|
61
|
+
"symptom": "an effect implementing app logic, or a dependency array edited to suppress reruns",
|
|
62
|
+
"fix": "move logic to render/handlers/key/useSyncExternalStore; treat exhaustive-deps as a build error"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"key": "rxc_no_waterfalls",
|
|
66
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
67
|
+
"found_by": "frontend/CLAUDE.md 'Render & bundle performance'",
|
|
68
|
+
"tags": "react",
|
|
69
|
+
"title": "No request waterfalls on the critical path — a fetch that waits on a render that waits on a fetch is the client-side N+1",
|
|
70
|
+
"body": "Route loaders start queries before render; independent fetches run in parallel (Promise.all / parallel queries); Suspense boundaries stream what's ready. Sequential render-then-fetch chains serialize latency exactly like the backend's N+1 query loop.",
|
|
71
|
+
"symptom": "a child component's fetch starting only after a parent's fetch resolves and renders",
|
|
72
|
+
"fix": "hoist fetches to route loaders; parallelize independent queries; stream with Suspense"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"key": "rxc_bundle_budget",
|
|
76
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
77
|
+
"found_by": "frontend/CLAUDE.md 'Render & bundle performance'",
|
|
78
|
+
"tags": "react",
|
|
79
|
+
"title": "Route-level code splitting is the default; the initial bundle budget is CI-checked; virtualize lists past ~50 items",
|
|
80
|
+
"body": "Heavy below-the-fold components load via dynamic import and third-party scripts load after hydration; the initial bundle budget (≤200 KB gz here) is enforced in CI, not aspirational. Long lists (product grids, order history) virtualize past ~50 items; non-urgent updates (search-as-you-type) go through startTransition/useDeferredValue to keep input latency flat.",
|
|
81
|
+
"symptom": "a route importing heavy components statically, or a list rendering hundreds of rows",
|
|
82
|
+
"fix": "dynamic-import below the fold, CI-check the bundle budget, virtualize long lists"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"key": "rxc_spa_auth_bff",
|
|
86
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
87
|
+
"found_by": "frontend/CLAUDE.md 'Security' (OAuth Browser-Based Apps BCP)",
|
|
88
|
+
"tags": "react,security",
|
|
89
|
+
"title": "SPA auth is authorization-code + PKCE, implicit flow is dead — and browser-held tokens are a documented trade-off that flips to BFF with real PII",
|
|
90
|
+
"body": "The OAuth Browser-Based Apps BCP makes PKCE a MUST for SPA public clients and deprecates response_type=token entirely; refresh tokens require rotation (or sender-constraining) with bounded lifetime. The BCP ranks browser-held tokens as its least secure pattern: acceptable for a demo with fake data, but the moment the app fronts real user data or payments, the BFF (tokens server-side, HttpOnly cookie to the browser) becomes the required shape — record the trade-off where the choice is made.",
|
|
91
|
+
"symptom": "an SPA using implicit flow, non-rotating refresh tokens, or browser-held tokens over real PII",
|
|
92
|
+
"fix": "auth-code + PKCE + rotated refresh tokens; migrate to BFF when the data becomes real"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"key": "rxc_no_client_money",
|
|
96
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
97
|
+
"found_by": "frontend/CLAUDE.md 'Security'",
|
|
98
|
+
"tags": "react,security",
|
|
99
|
+
"title": "The client never computes or trusts money/authorization fields — displayed totals are previews, the server's answer is authoritative",
|
|
100
|
+
"body": "The client-side counterpart of the server-controlled-fields rule: a cart's displayed total is a UI preview; the authoritative total comes back from the server's response to the write. Secrets never ship in the bundle — build-time env carries public config only; anything secret belongs behind an endpoint.",
|
|
101
|
+
"symptom": "client-computed totals submitted to (or trusted from) the API, or a secret in build-time env",
|
|
102
|
+
"fix": "display server-returned values; keep secrets behind endpoints"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"key": "rxc_test_behavior",
|
|
106
|
+
"type": "Rule", "scope": "org", "repo": "dotnet-microservices", "verified": true,
|
|
107
|
+
"found_by": "frontend/CLAUDE.md 'Testing'",
|
|
108
|
+
"tags": "react,method",
|
|
109
|
+
"title": "Test user-visible behavior at the network boundary — every feature ships happy + error + loading/empty tests",
|
|
110
|
+
"body": "Testing-library queries go by role/label and assert what the user sees — never hook internals or state shapes, which break on refactor without behavior change. Mocks live at the network boundary (MSW handlers mirroring real response shapes, including error and slow cases). The per-feature minimum is happy path + error path + loading/empty state; one E2E walk-through runs against the real stack as both regression gate and demo script.",
|
|
111
|
+
"symptom": "tests asserting hook state internals, or a feature shipping with only happy-path coverage",
|
|
112
|
+
"fix": "query by role/label, mock with MSW at the network edge, cover error and empty states per feature"
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"edges": []
|
|
116
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Deep-time / cross-domain modelling lessons from the geospatial repo Stage-2 (2026-08-06). Trying to map invasive extent back through the Landsat record exposed several portable ML failure modes. All WORLD-FACTS → org scope; they apply to any repo that applies a present-day-trained model to out-of-distribution data or reports a metric against its own training labels. Review, then `okl seed seed/geospatial-deeptime-defects.json`.",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"key": "r_resubstitution_not_validation",
|
|
6
|
+
"type": "Rule", "scope": "org", "repo": "geospatial-ml-pipeline", "verified": true,
|
|
7
|
+
"found_by": "CodeRabbit caught a map calling a 23% figure 'validated' when the model was trained on those labels",
|
|
8
|
+
"title": "A prediction reproducing its own training labels' proportions is IN-SAMPLE CALIBRATION, not validation",
|
|
9
|
+
"body": "\"The prediction matches the label truth\" is circular when the model was trained on those labels — you have only shown the model fits its training data (resubstitution). It is calibration, not validation. Independent validation requires a spatially/temporally HELD-OUT test the model never saw. This is easy to over-claim because the number really does match; the error is the word 'validated'.",
|
|
10
|
+
"symptom": "a result is called 'validated' because a predicted proportion/accuracy matches the labels the model was trained on",
|
|
11
|
+
"fix": "call it in-sample calibration; validate on a spatial/temporal holdout the model never trained on before claiming 'validated'",
|
|
12
|
+
"tags": "eval-integrity,method,data-quality"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"key": "d_cross_domain_recipe_dependence",
|
|
16
|
+
"type": "Defect", "scope": "org", "repo": "geospatial-ml-pipeline", "verified": true,
|
|
17
|
+
"found_by": "the invasive-over-time trajectory changed shape (5×-growth / flat-then-jump / decline-then-jump) across compositing recipes",
|
|
18
|
+
"title": "A present-day-trained model applied to out-of-distribution history yields a trajectory whose shape depends on the preprocessing recipe",
|
|
19
|
+
"body": "Applying a model trained on one era/domain to out-of-distribution data (older imagery, a different sensor) produces outputs highly sensitive to arbitrary preprocessing knobs — compositing window, scene count, normalization. The trend's *shape* can flip between equally-reasonable recipes, because OOD inputs land near the fixed decision boundary and flip on tiny feature shifts. Only the in-distribution anchor (the training year) is reliable; the further from it, the more recipe-dependent. Three fixes (multi-year windows, spectral indices, relative radiometric normalization) stabilised the near epochs but never the sensor-distant one.",
|
|
20
|
+
"symptom": "a deep-time / cross-domain trajectory changes shape when you vary a preprocessing knob (window width, scene count, normalization)",
|
|
21
|
+
"fix": "re-run with ≥2 preprocessing recipes and require agreement before claiming a trend; report only the in-distribution anchor as reliable; treat divergence as a 'not-reconstructable' verdict, not a knob to tune toward a prettier line",
|
|
22
|
+
"tags": "eval-integrity,geospatial,method"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"key": "g_multi_recipe_robustness",
|
|
26
|
+
"type": "Gate", "scope": "org", "repo": "geospatial-ml-pipeline", "verified": true,
|
|
27
|
+
"found_by": "bracket + multi-recipe re-runs turned a spurious '5× trajectory' into an honest not-reconstructable verdict",
|
|
28
|
+
"title": "Prove a cross-domain trend robust by re-running under ≥2 reasonable recipes before publishing it",
|
|
29
|
+
"body": "Before shipping any deep-time / transfer trajectory, run it under at least two defensible preprocessing recipes (e.g. 5-year vs 7-year windows, N vs 2N scenes). If the points move by more than the effect you want to claim, the trend is an artifact of the recipe — report the null, not the line. A single-recipe trajectory is unfalsified.",
|
|
30
|
+
"symptom": "a trajectory / trend is reported from a single preprocessing recipe with no robustness check",
|
|
31
|
+
"fix": "re-run under ≥2 recipes; require per-point agreement within the claimed effect size; else report not-reconstructable",
|
|
32
|
+
"tags": "eval-integrity,method"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"key": "r_mask_hierarchical_classifier",
|
|
36
|
+
"type": "Rule", "scope": "org", "repo": "geospatial-ml-pipeline", "verified": true,
|
|
37
|
+
"found_by": "an invasive-vs-native (within-woody) classifier fired on non-woody pixels; raw fraction was 65%, masked to the corridor it was 23%",
|
|
38
|
+
"title": "A classifier trained to separate A-vs-B only within parent class C is undefined outside C — mask it to a C-predictor",
|
|
39
|
+
"body": "A model trained to distinguish A from B using only class-C examples is undefined on non-C inputs: applied wall-to-wall it fires arbitrarily outside C. Two independent classifiers (a C-detector and an A-within-C detector) are NOT spatially guaranteed to nest. Before reporting any A/C proportion, intersect the A-predictor with the C-predictor (or C labels). The unmasked fraction can be wildly wrong (here 65% vs the true 23%).",
|
|
40
|
+
"symptom": "a subset-trained classifier is applied to a whole scene and its raw positive fraction is reported",
|
|
41
|
+
"fix": "mask the subset classifier's predictions to a parent-class predictor; report the proportion as (A ∩ C) / C",
|
|
42
|
+
"tags": "eval-integrity,data-quality,geospatial"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"key": "g_multiyear_window_composite",
|
|
46
|
+
"type": "Gate", "scope": "org", "repo": "geospatial-ml-pipeline", "verified": true,
|
|
47
|
+
"found_by": "single-year composites swung ±0.5 pp (1999/2000/2001 = 2.1/1.7/1.2%); 5-year windows collapsed the noise",
|
|
48
|
+
"title": "Composite over multi-year windows, not single years, for a time-series remote-sensing trend",
|
|
49
|
+
"body": "Single-year growing-season composites carry large year-to-year noise (cloud/atmosphere/phenology) — here ~±0.5 pp, enough to invent or erase a trend and to fake a one-year 'dip'. Median over a multi-year window (e.g. ±2 years, balanced across years) outvotes any single bad year. Bracket a suspicious point with its neighbours: if only it moves, it's a single-year artifact, not a signal.",
|
|
50
|
+
"symptom": "a per-year point in a satellite time series looks anomalous, or the trend is noisy year to year",
|
|
51
|
+
"fix": "composite over multi-year windows (balanced per-year scene quota); bracket outliers with ±1-year neighbours before interpreting",
|
|
52
|
+
"tags": "geospatial,eval-integrity"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"edges": [
|
|
56
|
+
{ "src": "g_multi_recipe_robustness", "rel": "CATCHES", "dst": "d_cross_domain_recipe_dependence" },
|
|
57
|
+
{ "src": "g_multiyear_window_composite", "rel": "CATCHES", "dst": "d_cross_domain_recipe_dependence" }
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Seed for the OKL — real, dated, receipted lessons from the geospatial pipeline's method.md, promoted to org scope so any repo's first `okl check` returns an earned lesson. Only WORLD-FACTS are org-scoped (portable gates, prior art, data-source gotchas); repo-only quirks are omitted or scoped repo:geospatial-ml-pipeline.",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"key": "d14",
|
|
6
|
+
"type": "Defect",
|
|
7
|
+
"scope": "org",
|
|
8
|
+
"repo": "geospatial-ml-pipeline",
|
|
9
|
+
"found_by": "check-scaffold-classpaths.sh (importing all 23 mechanically)",
|
|
10
|
+
"verified": true,
|
|
11
|
+
"title": "rslearn/OlmoEarth scaffold class_paths written from memory, never imported",
|
|
12
|
+
"body": "5 of 23 class_paths did not exist. Every class NAME right, every MODULE path wrong; the spec asserted they were 'correct'. They fail at runner startup — Phase 1, on a rented GPU. Any repo scaffolding an rslearn/OlmoEarth model config is exposed.",
|
|
13
|
+
"tags": "geospatial"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"key": "g_classpath",
|
|
17
|
+
"type": "Gate",
|
|
18
|
+
"scope": "org",
|
|
19
|
+
"found_by": "the geospatial pipeline",
|
|
20
|
+
"verified": true,
|
|
21
|
+
"title": "check-scaffold-classpaths.sh — import every declared class_path mechanically",
|
|
22
|
+
"body": "Portable Tier-3 gate: parse the scaffold config, import all class_paths, fail if any is unresolvable. Arm this in any repo that scaffolds rslearn/OlmoEarth/model YAML before it writes a class_path.",
|
|
23
|
+
"tags": "geospatial",
|
|
24
|
+
"repo": "geospatial-ml-pipeline"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"key": "d15",
|
|
28
|
+
"type": "Defect",
|
|
29
|
+
"scope": "org",
|
|
30
|
+
"repo": "geospatial-ml-pipeline",
|
|
31
|
+
"found_by": "verify_materialized() — check rasters on disk, not exit code",
|
|
32
|
+
"verified": true,
|
|
33
|
+
"title": "rslearn dataset materialize exited 0 having written zero files",
|
|
34
|
+
"body": "NotImplementedError on all 238 windows swallowed into a worker pool. 'ingest: false' needs get_item_by_name, which Planetary Computer's Sentinel2 raises by design. Never trust an exit code — verify the outputs exist.",
|
|
35
|
+
"tags": "geospatial,data-quality"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"key": "g_verify_outputs",
|
|
39
|
+
"type": "Gate",
|
|
40
|
+
"scope": "org",
|
|
41
|
+
"found_by": "the geospatial pipeline",
|
|
42
|
+
"verified": true,
|
|
43
|
+
"title": "verify_materialized() — assert output artifacts exist, never trust exit 0",
|
|
44
|
+
"body": "Portable pattern: after any batch/pipeline step, assert the expected files are on disk with non-zero size. Exit code 0 with zero outputs is a common swallowed-exception failure.",
|
|
45
|
+
"tags": "method,data-quality",
|
|
46
|
+
"repo": "geospatial-ml-pipeline"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"key": "d16",
|
|
50
|
+
"type": "Defect",
|
|
51
|
+
"scope": "org",
|
|
52
|
+
"repo": "geospatial-ml-pipeline",
|
|
53
|
+
"found_by": "pinning TMPDIR + CPL_TMPDIR to the data drive",
|
|
54
|
+
"verified": true,
|
|
55
|
+
"title": "GDAL temp files (CPL_TMPDIR) not covered by TMPDIR — filled boot disk to zero",
|
|
56
|
+
"body": "TMPDIR on the data drive still let materialize write 2.8 GB to /. GDAL keeps its own temp, CPL_TMPDIR. Redirect EVERY temp mechanism, not the first one you find. Storage estimate was also ~10x low for ingest (11 GB tile store vs '~1.2 GB' materialised).",
|
|
57
|
+
"tags": "geospatial"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"key": "d18",
|
|
61
|
+
"type": "Defect",
|
|
62
|
+
"scope": "org",
|
|
63
|
+
"repo": "geospatial-ml-pipeline",
|
|
64
|
+
"found_by": "tests/test_class_scheme_contract.py",
|
|
65
|
+
"verified": true,
|
|
66
|
+
"title": "num_classes off-by-one when class 0 is reserved (zero_is_invalid)",
|
|
67
|
+
"body": "model.yaml said num_classes: 4; crosswalk emits 4 real classes and class 0 is reserved, so it needs 5. Crashed 'Target 4 is out of bounds'. Contract test: num_classes == max crosswalk id + 1.",
|
|
68
|
+
"tags": "geospatial"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"key": "d19",
|
|
72
|
+
"type": "Defect",
|
|
73
|
+
"scope": "org",
|
|
74
|
+
"repo": "geospatial-ml-pipeline",
|
|
75
|
+
"found_by": "laptop dry-run before GPU",
|
|
76
|
+
"verified": true,
|
|
77
|
+
"title": "SegmentationPoolingDecoder reads spatial dims wrong on a temporal cube",
|
|
78
|
+
"body": "Decoder reads image.shape[1:3]; a 4-D [bands, timesteps, H, W] input makes it grab (timesteps, H) and predict 12x2 against a 2x2 target. Temporal segmentation needs a decoder reading the true last-two axes. Dry-run on CPU before spending GPU.",
|
|
79
|
+
"tags": "geospatial"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"key": "c1",
|
|
83
|
+
"type": "Claim",
|
|
84
|
+
"scope": "org",
|
|
85
|
+
"repo": "geospatial-ml-pipeline",
|
|
86
|
+
"status": "retracted",
|
|
87
|
+
"found_by": "/paper-audit",
|
|
88
|
+
"title": "Novelty claim: 'CSU produced points but no map; nobody has mapped the target species change in the study basin'",
|
|
89
|
+
"body": "Recorded from the geospatial pipeline method.md (error #5), not independently re-verified here. Per that audit, the claim was retracted because a prior-art paper (attributed there to Evangelista et al., 2018) reportedly shipped 2-epoch change maps incl. the target species in the study basin. Before relying on this in another repo, VERIFY the citation against the literature via `okl check` + a paper audit.",
|
|
90
|
+
"tags": "method"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"key": "pa1",
|
|
94
|
+
"type": "PriorArt",
|
|
95
|
+
"scope": "org",
|
|
96
|
+
"status": "live",
|
|
97
|
+
"verified": true,
|
|
98
|
+
"found_by": "/paper-audit (recorded in the geospatial repo method.md, not re-verified here)",
|
|
99
|
+
"title": "Prior art (attributed to Evangelista et al., 2018) — 2-epoch the target species change maps, the study basin",
|
|
100
|
+
"body": "THREAT verdict recorded in the geospatial pipeline: reportedly refutes the 'nobody has mapped this' novelty claim; the same source also cites CO-RIP geospatial-extent mapping. Citation transcribed from that repo, NOT independently confirmed — verify against the literature before relying on it elsewhere.",
|
|
101
|
+
"tags": "method",
|
|
102
|
+
"repo": "geospatial-ml-pipeline"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"key": "d12",
|
|
106
|
+
"type": "Defect",
|
|
107
|
+
"scope": "org",
|
|
108
|
+
"repo": "geospatial-ml-pipeline",
|
|
109
|
+
"found_by": "counting before trusting",
|
|
110
|
+
"verified": true,
|
|
111
|
+
"title": "Public dataset had x/y transposed — 119 points in the wrong hemisphere",
|
|
112
|
+
"body": "Virgin_River rows had coordinates transposed. Count / plot points on a map before trusting any public geospatial dataset's coordinate order.",
|
|
113
|
+
"tags": "geospatial,data-quality"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"key": "d11",
|
|
117
|
+
"type": "Defect",
|
|
118
|
+
"scope": "org",
|
|
119
|
+
"repo": "geospatial-ml-pipeline",
|
|
120
|
+
"found_by": "refusing a convenient bad number",
|
|
121
|
+
"verified": true,
|
|
122
|
+
"title": "AUC 0.23 looked like a broken encoder; was unshuffled KFold on a spatial grid",
|
|
123
|
+
"body": "Shuffled KFold: 0.85. A convenient bad number (looks like the thing you feared) is exactly the one to distrust. Use spatial/grouped CV on spatially autocorrelated data.",
|
|
124
|
+
"tags": "geospatial,eval-integrity"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"edges": [
|
|
128
|
+
{
|
|
129
|
+
"src": "g_classpath",
|
|
130
|
+
"rel": "CATCHES",
|
|
131
|
+
"dst": "d14"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"src": "g_classpath",
|
|
135
|
+
"rel": "VERIFIED_ON",
|
|
136
|
+
"dst": "d14"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"src": "g_verify_outputs",
|
|
140
|
+
"rel": "CATCHES",
|
|
141
|
+
"dst": "d15"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"src": "g_verify_outputs",
|
|
145
|
+
"rel": "VERIFIED_ON",
|
|
146
|
+
"dst": "d15"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"src": "pa1",
|
|
150
|
+
"rel": "REFUTES",
|
|
151
|
+
"dst": "c1"
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|