feat(causal): Phases 1–5 — check/analyze/diff, evidence, expectations, observation, ledger#369
Conversation
…(Phase 1) Implement the review-only causal profile per docs/DESIGN-causal.md (issue ymm-oss#321): - rust/fsl-syntax/src/causal.rs: standalone parser for causal Name {...} documents. Deliberately NOT registered in the frontends! dialect registry — the frozen Python DIALECT_KEYWORDS parity gate does not move; consumers detect causal documents with the pre-dispatch is_causal_source sniff (the is_ai_project pattern). - rust/fsl-tools: typed CausalModel (roles, action/kpi/state/property bindings resolved against imported specs, closed scope vocabulary with contradiction rejection, claim content versions, active|retired lifecycle, clock mappings, fail-closed diagnostics incl. causal_instantaneous_loop and causal_unacknowledged_feedback); SCC-condensed analyses (Dijkstra-exact earliest first-pass, upper-bounded latest through feedback SCCs, polarity sign products with unknown absorption, cut-claim bottlenecks, loop_class reinforcing|balancing|unknown with claim-ID witnesses); 15 causal-review findings, every one formal_status not_a_violation with do_not_assume; causal_graph/causal_timeline/causal_traceability_graph projections with capped representative paths + truncation metadata; Mermaid/DOT export; causal diff keyed on stable claim IDs and content versions with support_transition not_available (until ymm-oss#322). - rust/fslc: causal command family; fslc check auto-routes causal documents; error envelope carries diagnostic kind + loc; exit codes 0/2 as elsewhere. There is deliberately no 'causal verify'. - LSP: source_diagnostic.rs and DocumentIndex::build apply the causal sniff, so causal files never surface FSL-DIALECT-UNKNOWN. - schemas/fslc/causal/{causal-check,causal-graph,causal-findings, causal-diff}.v0.schema.json; native_integration schema inventory 27->31. - examples/causal/: three dogfood models (subscription retention with a reinforcing feedback loop, incident response with deadline/cadence findings, marketing funnel with opposing polarity and a declared common-cause fork) plus business/kernel companions; each yields actionable findings (AC 9). - docs/LANGUAGE.md §17 + docs/LANGUAGE.ja.md §17 (section-aligned, site regenerated), skills/fsl/reference.md §12 with the agent-facing never-present-causal-claims-as-proved hard rule, DESIGN-causal.md §12 Phase 1 implementation notes (deadline/window fields, thresholds, timeline bound semantics, Worker waiver), CHANGELOG. - Tests: fsl-syntax parser suite, 29 fsl-tools model/analysis tests, 13 CLI contract tests (incl. determinism, fail-closed unknown refs, no-causal-verify, review-only assertions), causal_docs_contract. fsl-runtime, fsl-solver*, fsl-verifier, Public Kernel v1, and the frozen Python reference are untouched. Closes ymm-oss#321 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coupled-change review follow-ups: per-command schemas conventionally require the top-level "fsl" envelope field (cf. analysis-findings.v0), and the traceability projection emits "state" measurement-binding nodes. Add both to the four causal v0 schemas; outputs of all three dogfood models across check, every projection, and causal-review now shape-validate against their schemas. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uments The browser parity corpus collects every .fsl under examples/ and feeds check/verify through both the native CLI and the Worker. Standalone causal documents are CLI-only by explicit waiver (docs/DESIGN-causal.md §12) — the Worker exposes only check/verify kernel surfaces — so treat 'causal' exactly like the existing 'refinement'/'agent' unsupported classifications: detected via the declaration keyword (the surface classifier rejects dispatch-bypassing documents), gated by the reviewed unsupportedDocuments map so an unreviewed causal file still fails loudly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ss checks Implement the causal profile's evidence plane (Phase 2, issue ymm-oss#322) per docs/DESIGN-causal.md: - rust/fsl-tools/src/causal_evidence.rs: fsl-causal-evidence.v0 artifact validation (closed design/support vocabularies, claim ID + content version pins, canonical-JSON sha256 artifact_digest, reserved observation object that forces design observational + support inconclusive with a closed field set); independent fsl-causal-evidence-lifecycle.v0 append-only chains (gapless sequences, digest-linked records, terminal retracted/superseded, resolvable superseded_by, tamper detection). Schema/digest/chain violations fail closed with exit 2. - Deterministic support aggregation: only artifacts pinning the current claim version, with subsumes scope (three-valued comparison over the declared scope vocabulary), declared freshness, active lifecycle, and an observation window >= the claim's minimum lag are counted; source lineages (derived_from root or source_study_id) collapse to one vote, contradictions inside a lineage become inconclusive. Result vocabulary: untested | supported | challenged | inconclusive | mixed | unsupported_by_current_evidence. Staleness is judged only against an explicit --as-of date, never the wall clock. - 16 evidence/measurement review findings (evidence_scope_*, evidence_window_shorter_than_lag with </==/not-evaluable boundaries, duplicate_evidence_source, unknown_freshness/stale_evidence, unknown_lifecycle/retracted_or_superseded_evidence, evidence_claim_version_mismatch, evidence_claim_mismatch, observational_only_support, conflicting_evidence, plus model-level unobserved_mediator / proxy_only_critical_variable / unsupported_assumption_chain), all formal_status not_a_violation. - CLI: repeatable --evidence/--lifecycle and --as-of on causal analyze; new causal_evidence_graph projection (causal-evidence-graph.v0) whose claims always carry formal_assurance "not_run" next to causal_support; causal-review with evidence appends findings and a causal_support map. - Schemas: fsl-causal-evidence.v0, fsl-causal-evidence-lifecycle.v0, causal-evidence-graph.v0; causal-findings.v0 enum extended; inventory 31 -> 34. - examples/causal/evidence/: digest-stamped artifact + active lifecycle chain; the retention model's evidenced claim now demonstrates supported with subsumes scope. - docs/LANGUAGE.md + docs/LANGUAGE.ja.md §17 extended (site regenerated), skills/fsl/reference.md agent rules for the two-axis separation, DESIGN-causal.md §13 Phase-2 notes, CHANGELOG. - Tests: 12 evidence unit tests (digest tamper, closed enums, observation rules, lifecycle gap/fork/tamper/terminal/supersession, civil-date conversion, support table, as-of staleness, lineage collapse, window boundaries, observational-only) + 5 CLI tests (overlay without formal_assurance change, unknown lifecycle exclusion, --as-of staleness, digest tamper fail-closed, review support map). Evidence never changes formal assurance; fsl-runtime, fsl-solver*, fsl-verifier, Public Kernel v1, and the frozen Python reference are untouched. Closes ymm-oss#322 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed three follow-up commits; this PR now covers Phase 2 (#322) as well as Phase 1 (#321), since the work stacks on the same branch:
Invariant held by test in both phases: evidence never changes Evidence: workspace fmt/clippy/tests green (86/86 binaries); |
…kernel leadsTo Implement Phase 3 of the causal profile (issue ymm-oss#323) per docs/DESIGN-causal.md: - fsl-syntax: expectation declarations in the causal parser (trigger action alias.name | trigger predicate alias { <expr> }, response predicate alias { <expr> }, within N, clock <name>, derived_from_claim). Inline predicate blocks capture raw kernel expression source (brace-matched); the legacy 'supports' field name is a parse error. - fsl-tools/src/causal_expectation.rs: fail-closed expectation compiler. Gates: missing trigger/response/within/clock, unknown clock, trigger or response alias != the clock's kernel spec (a clock is never applied to a different spec), unresolved derived_from_claim, non-kernel-spec targets, absent trigger actions, and within values that do not convert to an exact integer tick count (nothing is rounded). Action triggers lower to a one-step pulse ghost (_expectation_fired_<id>: Bool, set true by the trigger action, false by every other action) so the generated leadsTo means 'within N ticks of the action firing' without touching guards or deadlock behavior. Predicates type-check against the target state space via build_surface_model — KPI deltas/averages fail closed there. Claims themselves are never lowered. - fslc: causal verify-expectations subcommand (--depth K, default 8) runs the ordinary bounded verifier per expectation (causal-expectations.v0). A violation of the generated property is verdict 'violated'; a violation of any pre-existing property is an error so expectation verdicts never absorb baseline bugs. Pass and violated goldens both hold the central invariant: every claim stays formal_assurance "not_run" with untouched causal_support; derived_from_claim is two-way traceability only. - examples/causal/incident_response.fsl: clock + one passing and one violated expectation (dogfood goldens). - schemas/fslc/causal/causal-expectations.v0.schema.json (inventory 35), docs/LANGUAGE.md + docs/LANGUAGE.ja.md §17 (site regenerated), skills/fsl/reference.md agent rule ('never summarize an expectation verdict as the causal claim's status'), DESIGN-causal.md §14 Phase-3 notes, CHANGELOG. - Tests: pass/violated goldens with claim-axis invariants, clock missing/fractional fail-closed, legacy supports rejection, unresolved response fail-closed at build time, and semantic equivalence of the generated property with a hand-written pulse encoding via ordinary fslc verify. fsl-runtime, fsl-solver*, fsl-verifier semantics, Public Kernel v1, and the frozen Python reference are untouched (the verifier is only called, never changed). Closes ymm-oss#323 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed Phase 3 (#323) — causal expectation lowering — onto this branch:
Evidence: workspace fmt/clippy/tests green (86/86 binaries; causal CLI suite now 22 tests); |
… for observational evidence (ymm-oss#360) Bridge the causal expectation plane with the evidence plane by replaying compiled expectations against production JSONL logs using the solver-free BoundedLivenessMonitor. Generates per-expectation fsl-causal-evidence.v0 artifacts with design:observational, support:inconclusive, assurance:replay-observed. Pass/violated verdicts never change claim formal_assurance or causal_support. Nonconformant logs, missing scope/period, and mapping failures are fail-closed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-phase1 # Conflicts: # CHANGELOG.md
Satisfies the manual_is_multiple_of lint introduced in Clippy 1.95. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion reasons (ymm-oss#364) Add fslc causal ledger integrating claims, validation plans, evidence, and observations into a per-claim projection with 12 deterministic attention reasons (validation_plan_missing, current_evidence_missing, observation_not_directional_support, etc.). New fsl-causal-validation-plan.v0 schema for immutable plan artifacts; plan lifecycle reuses fsl-causal-evidence-lifecycle.v0. Every claim appears regardless of plan/evidence; retired claims have no attention; output is byte-stable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rizumita
left a comment
There was a problem hiding this comment.
Ran a 4-pronged review (coupled-change, soundness, vacuity, and a design-conformance pass against docs/DESIGN-causal.md) against the PR worktree (commit f871c93). Summary — the core "never attach proved/verified to a causal claim" firewall holds everywhere I could exercise it (the observation-bridge evidence and the expectation-compiler output both correctly stay at not_run/inconclusive across generation and validation), and all causal-specific tests pass (causal_cli 28/28, fsl-tools 51/51, fsl-syntax 41/41, causal_docs_contract 2/2). But there's one concrete shipped bug and a few gaps worth a fast-follow before this leaves draft:
Bug: rust/fsl-tools/src/causal.rs's validate_loops (~1199-1208) has a doc comment stating unknown-lag edges should be included in the zero-lag-cycle check ("cannot prove a positive delay"), but the code does Lag::Unknown => false, excluding them. A cycle composed entirely of lag: unknown edges passes causal check without the causal_instantaneous_loop error the comment says it should get. No test exercises this case. Cheap, concrete fix.
Gaps (non-blocking, advisory-axis only):
rust/fsl-lsp/src/index.rs's +4/-1 diff only suppresses a parser crash; it doesn't addcausal/claim/variable/etc. todeclaration_keyword/INDEX_KEYWORDS, so go-to-definition/hover/document-symbols don't work for causal blocks yet.formal_assurance:"not_run"/causal_support:"untested"are independently hardcoded string literals at 10+ call sites with no shared constructor/type enforcing them — correct everywhere today, but nothing structurally prevents a future site from typing something else.- Claim-version/retired-lifecycle enforcement and evidence-side extra-dimension handling inherit the same gaps flagged in the #367 design review (both only affect the advisory
causal_supportaxis, not the proved/verified firewall). - Minor: the PR description's "15 causal-review findings" — I only observed 8 total findings across the 3 real causal-model examples; worth reconciling in the description.
Nothing here blocks the phase 1–4 architecture; recommend fixing the lag: unknown loop bug before this comes out of draft, and filing the LSP-index and claim-axis-typing gaps as fast-follow issues.
…tom schema field Adversarial review findings: - Plan lifecycle chains now verify artifact_digest matches the plan's declared digest (fail-closed on mismatch). - Removed undeclared `expectations` property from causal-ledger.v0 claim_entry schema (code never emitted it). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A cycle composed entirely of lag:unknown edges was silently accepted because validate_loops excluded unknown edges. The doc comment already stated unknown lag cannot prove a positive delay — align the code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…als with constants Review findings: - Register causal/claim/variable/expectation in LSP declaration_keyword and causal-specific tokens in INDEX_KEYWORDS so go-to-definition/hover work for causal blocks. - Replace 10+ hardcoded "not_run"/"untested" string literals with shared FORMAL_ASSURANCE_NOT_RUN / CAUSAL_SUPPORT_UNTESTED constants from fsl-tools, preventing future typo drift. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@rizumita レビューありがとうございます。指摘3件すべて修正しました: Bug:
|
Satisfies the manual_assert_eq lint added in Clippy 1.97. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-phase1 # Conflicts: # CHANGELOG.md
…e, authority, and lag:unknown Review findings from @rizumita: 1. Claim version/retired lifecycle: content changes without version bumps are now detectable via causal diff; retired is terminal (no retired→active); re-proposal detection via source/target/polarity match with retired claims. 2. Authority scope: §1 now explicitly scopes normative authority per phase and clarifies that observation replay never converts temporal co-occurrence into directed support, and expectation verdicts label the kernel property not the causal claim. 3. confidence field: clarified as deterministic structural score, not a probability or Bayesian posterior. 4. basis: explicitly version-relevant content requiring a version bump. 5. lag:unknown in feedback cycles: documented as treated like lag.min==0 (cannot prove positive delay); all-unknown cycles are instantaneous errors. 6. Missing scope dimensions: specified as unassessable per dimension; empty scope is never silently universal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ss#364 AC gaps ymm-oss#360: - observe_expectations_rejects_short_observation_window (AC6) - observe_expectations_claim_version_change_does_not_auto_update_support (AC6) ymm-oss#364: - ledger_retired_claim_has_no_attention_but_appears (AC10) - ledger_plan_version_mismatch_fires_attention (AC3/AC13) - ledger_observation_evidence_stays_inconclusive_and_never_directed (AC8) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generated observation evidence lacked valid_until, causing immediate exclusion by the evidence overlay's freshness check. Set valid_until to period_end so artifacts are applicable when consumed by fslc causal analyze --evidence --as-of within the observation period. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # rust/fslc/tests/native_integration.rs
rizumita
left a comment
There was a problem hiding this comment.
再レビュー済みです。content_changed_without_version_bump、retired claim の再活性化・再提案検出、片側だけの scope dimension の unassessable 判定が実装・schema・英日ドキュメントで整合しました。observation window と claim version の負例も生成 artifact を evidence overlay に再投入して除外を検出する形になっています。最新 main との競合および Claim 公開名衝突も解消され、ローカルの完全 native integration gate と GitHub CI 5件すべて成功を確認しました。
# Conflicts: # CHANGELOG.md
Summary (issues #321, #322, #323, #360, #364)
Phases 1–5 of the review-only causal profile per
docs/DESIGN-causal.md(stacked on PR #367; merge #367 first or merge this PR to get both).FSL never proves real-world causality. Every causal output carries
formal_result: "not_run"anddo_not_assume; no output path attachesproved/verifiedto a claim; there is deliberately nocausal verify(asserted by test).What's included
Phase 1 (#321): check/analyze/diff
fslc causal check|analyze|diff, LSP sniff, Worker waiver.Phase 2 (#322): evidence overlay
fsl-causal-evidence.v0artifacts, lifecycle chains, deterministic per-claimcausal_support, 16 evidence/measurement findings,--evidence/--lifecycle/--as-ofCLI.Phase 3 (#323): expectation lowering
expectationcompiler →leadsTo ... within tickswith pulse ghost for action triggers;fslc causal verify-expectations(causal-expectations.v0).Phase 4 (#360): observation bridge
fslc causal observe-expectationsreplays expectations against production JSONL logs usingBoundedLivenessMonitor; generatesfsl-causal-evidence.v0artifacts withdesign: "observational",support: "inconclusive",assurance: "replay-observed".Phase 5 (#364): portfolio ledger
fslc causal ledgerintegrates claims, validation plans, evidence, and observations into a per-claim projection with 12 deterministic attention reasons (validation_plan_missing,current_evidence_missing,observation_not_directional_support, etc.).fsl-causal-validation-plan.v0schema for immutable plan artifacts with claim pins, design, scope, observation window, measurements, and opaque external refs.fsl-causal-evidence-lifecycle.v0.Not changed
rust/fsl-runtime,rust/fsl-solver*,rust/fsl-verifier, Public Kernel v1,src/fslc/(frozen Python).Evidence
cargo fmt --check/ workspaceclippy -D warningscleancargo test -p fslc-rust --test causal_cli— 33 tests ok (5 ledger + 5 observe + 23 existing)formal_assurance: "not_run"andcausal_supporthold across all verdictsCloses #321
Closes #322
Closes #323
Closes #360
Closes #364
🤖 Generated with Claude Code