Append-Only Timeline Fact Corrections #29

Closed
opened 2026-09-22 17:58:58 +00:00 by yuris · 0 comments
Owner

Summary

Implement append-only timeline corrections (POST /v1/captures with kind: "correction", and life timeline [--corrected]), appending new corrective events into SQLite life_events referencing earlier events via correction_target, strictly enforcing immutable table triggers, rejecting attempts to rewrite confirmed transport facts or create cyclic references (invalid_input), and providing both raw chronological and folded corrected timeline query views.

Background & Technical Rationale

Human memory and initial reporting are fallible. Users will inevitably report inaccurate durations, misattribute work blocks, or record mistaken outcomes. In conventional systems, corrections are handled by performing destructive SQL UPDATE or DELETE operations on historical rows. This approach creates severe systemic failure modes:

  1. Destruction of Audit History: Destructive updates destroy the record of what the user originally reported, corrupting provenance and making it impossible to reconstruct why an earlier plan or recommendation was made.
  2. Cache and Outbox Invalidation: Background processors and external sync workers relying on immutable sequence numbers will encounter corrupted state or missing records.
  3. Falsification of Transport Realities: Allowing a user correction to alter an acknowledged CalDAV transaction row (evidence_class: "confirmed_service") falsifies the historical fact of what was actually transmitted to and acknowledged by the remote server.

Life guarantees historical integrity through an append-only architecture:

  • Strict Database Triggers: SQLite table life_events is guarded by triggers life_events_no_update and life_events_no_delete that raise ABORT. No application code or administrator query can update or delete rows through normal operations.
  • Corrections as New Linked Records: A correction appends a NEW row to life_events where correction_target stores the event_id of the target record. The target record remains unaltered in storage (Scenario W14).
  • Zero CalDAV Mutation: Correcting a timeline event does NOT update Radicale CalDAV calendar components. Fixing live item state requires a separate conditional item mutation (e.g. life task update).
  • Transport Fact Protection (Scenario R13): Only user assertions (evidence_class: "user_report") and derived interpretations (evidence_class: "derived") are correctable. Transport confirmations (evidence_class: "confirmed_service") represent verified physical network transactions and cannot be corrected via capture.
  • Graph Invariants & Cycle Detection (Scenario R13): The engine verifies that correction_target exists and performs cycle detection to prevent A \to B \to A reference loops. Invalid targets or cycles are rejected with invalid_input (HTTP 400 / Exit 2).
  • Dual Query Views: Callers can query raw timeline facts (--view raw) or a folded corrected view (--view corrected) that computes the effective state by following correction chains while exposing original event IDs and provenance.

Exact Technical References & Specifications

1. Timeline Consistency & Correction Semantics (docs/technical/timeline-consistency.md)

Section: ## Context delivery and correction (lines 160–165):

Correction appends a user assertion referencing correction_target; corrected views apply the latest valid correction chain in recorded sequence order and show provenance. Reject cycles, cross-type corrections and attempts to rewrite confirmed transport facts. Raw views retain originals. Append-only correction does not erase sensitive content; exceptional erasure and backup consequences belong to Operations.

Section: ## Illustrative schema (lines 37–62):

CREATE TABLE life_events (
  sequence INTEGER PRIMARY KEY AUTOINCREMENT,
  event_id TEXT NOT NULL UNIQUE,
  dedupe_key TEXT NOT NULL UNIQUE,
  schema_version INTEGER NOT NULL,
  kind TEXT NOT NULL,
  evidence_class TEXT NOT NULL CHECK(evidence_class IN
    ('confirmed_service','observed_external','user_report','derived')),
  recorded_at TEXT NOT NULL,
  occurred_at TEXT,
  actor TEXT,
  source TEXT NOT NULL,
  collection_id TEXT,
  uid TEXT,
  recurrence_key TEXT,
  before_json TEXT,
  after_json TEXT,
  reason TEXT,
  provenance_json TEXT NOT NULL,
  operation_id TEXT REFERENCES operations(operation_id),
  correction_target TEXT REFERENCES life_events(event_id)
);
CREATE TRIGGER life_events_no_update BEFORE UPDATE ON life_events
BEGIN SELECT RAISE(ABORT, 'append-only events'); END;
CREATE TRIGGER life_events_no_delete BEFORE DELETE ON life_events
BEGIN SELECT RAISE(ABORT, 'append-only events'); END;

2. Workflows Specification (docs/technical/workflows.md)

Section: ## Lifecycle and review (line 38):

| Workflow | Input and reads | Decision and approval | Writes and observable result | Failure path |
|---|---|---|---|---|
| Correction | Target timeline event, corrected assertion, reason and evidence | Explicit user assertion; distinguish fact correction from calendar mutation | Structured capture appends correction referencing original; corrected view retains provenance | Missing target rejected; correcting timeline alone never updates Radicale |

Section: ## Capture and retrieval (line 20):

| Timeline query | Bounded record-time range, optional occurred-time range/item/evidence class | Query SQLite; corrections folded only when requested, originals remain available | Cursor-paginated facts with coverage and actual-time uncertainty | Missing observation interval is unknown, not zero activity |

Section: ## Weekly analytics (line 56):

Correction-aware totals identify the correction policy and retain links to original evidence.

3. CLI and API Route & Payloads (docs/technical/cli-api.md)

Section: ## Command-to-route table (lines 40, 48):

| `life capture --input -` | POST /v1/captures | write |
| `life timeline` | GET /v1/timeline | read |

Section: ## Command payloads and results (lines 87, 95, 102):

| Capture | kind task/event/context/outcome/correction; payload with corresponding fields; optional context for actionable capture | operation_id, per-part results and delivery state; partial parts remain explicit |
| Timeline | Default last 7 local days by recorded_at; optional from/to,item_id,evidence_class,occurred_from/to,view raw/corrected | Paginated timeline events, coverage, unknown-time count; schema |

Outcome capture payload: item_id, outcome (domain enum), optional occurred_at, actual_minutes, qualifies_career_floor (explicit user assertion), reason. Correction payload: target_event_id, replacement (same assertion type as target), reason. Only user assertions and derived interpretations may be corrected through capture; confirmed transport facts cannot be rewritten into different remote results. Fixing item state uses a separate conditional item mutation.

Section: ## Error identifiers (line 110):

| invalid_input, configuration_invalid | Correct request/configuration before retry |

4. Verification Scenarios W14 & R13 (docs/technical/verification.md)

Section: ## Workflow scenarios (line 28):

| ID | Requirement and specification | Input / action | Observable acceptance |
|---|---|---|---|
| W14 | Corrections (timeline-consistency.md#context-delivery-and-correction) | Correct a user report and query raw/corrected views | New linked event; original retained; corrected view provenance; Radicale unchanged |

Section: ## Recovery scenarios (line 99):

| ID | Boundary / injected failure | Observable acceptance |
|---|---|---|
| R13 | Correction tries to rewrite confirmed transport fact or form cycle | invalid_input; append-only original remains intact |

What to build

1. Correction Validator & Cycle Detector (src/domain/timeline/correction.rs)

  • Validate correction payload:
    • Input JSON:
      {
        "kind": "correction",
        "payload": {
          "target_event_id": "evt_out_01HN...",
          "replacement": {
            "outcome": "attended",
            "actual_minutes": 45,
            "qualifies_career_floor": true
          },
          "reason": "Correcting previously misreported duration"
        }
      }
      
  • Verify target event existence:
    • Query life_events WHERE event_id = ?.
    • If not found, return 404 Not Found with code: "not_found".
  • Verify evidence class:
    • If target.evidence_class == "confirmed_service":
      • Reject immediately with 400 Bad Request and code: "invalid_input" ("Cannot correct confirmed transport facts") (verifying Scenario R13).
    • Only allow correction if target.evidence_class IN ('user_report', 'derived').
  • Verify semantic match:
    • The replacement JSON structure must match the semantic kind of target.kind.
  • Perform Cycle Detection:
    • Traverse existing correction links backwards: starting from target_event_id, follow correction_target references.
    • If the new correction would introduce a loop (e.g. A \to B \to A), reject immediately with 400 Bad Request and code: "invalid_input" (verifying Scenario R13).

2. Timeline Correction Appender (src/storage/timeline_repo.rs)

  • Construct new life_events row:
    • event_id: Unique prefixed ID (evt_cor_...).
    • dedupe_key: Derived from operation_id (op_cor_...:fact:correction).
    • schema_version: 1.
    • kind: target.kind (or "correction").
    • evidence_class: "user_report".
    • recorded_at: Current UTC timestamp.
    • occurred_at: replacement.occurred_at if provided, else target.occurred_at.
    • actor: "user".
    • source: "cli_api".
    • collection_id: target.collection_id.
    • uid: target.uid.
    • before_json: target.after_json.
    • after_json: Serialized replacement payload.
    • reason: The user-provided reason string.
    • provenance_json: Metadata including client request ID and target_event_id.
    • operation_id: Current operation ID.
    • correction_target: target.event_id.
  • Execute SQL insert into life_events.
  • Invariant: Zero writes or modifications to Radicale CalDAV storage (verifying Scenario W14).

3. Timeline Query Projection Engine (src/domain/timeline/projection.rs)

  • Implement GET /v1/timeline:
    • Query parameters:
      • from, to: ISO-8601 timestamps filtering by recorded_at (default: last 7 local days).
      • occurred_from, occurred_to: Optional ISO-8601 timestamps filtering strictly by occurred_at.
      • view: Enum "raw" | "corrected" (default: "raw").
      • item_id: Optional filter.
      • evidence_class: Optional filter.
      • cursor: Opaque pagination cursor.
      • limit: Integer (default 50, max 200).
    • Response data envelope contains:
      • events: Paginated list of timeline items.
      • next_cursor: Null at end.
      • coverage: Temporal observation coverage metadata.
      • unknown_time_count: Count of returned events with unknown occurred_at.
  • If view == "raw":
    • Return all life_events ordered by sequence ASC (or recorded_at).
    • Includes both original rows and corrective rows.
  • If view == "corrected":
    • Fetch all relevant events in the query interval plus their correction chains.
    • Fold corrections: For any event targeted by one or more valid corrections, apply the latest correction in sequence order.
    • Replace the effective data with the latest correction's after_json.
    • Annotate the returned item with correction metadata:
      {
        "event_id": "evt_cor_01HN8...",
        "original_event_id": "evt_out_01HN7...",
        "correction_chain_length": 1,
        "kind": "work_block_outcome",
        "evidence_class": "user_report",
        "data": {
          "outcome": "attended",
          "actual_minutes": 45,
          "qualifies_career_floor": true
        },
        "recorded_at": "2026-09-23T12:00:00Z",
        "provenance": {
          "corrected_at": "2026-09-23T12:00:00Z",
          "reason": "Correcting previously misreported duration"
        }
      }
      

4. CLI Presentation (src/cli/timeline.rs)

  • Implement CLI flags:
    life timeline [--from ISO] [--to ISO] [--occurred-from ISO] [--occurred-to ISO] [--corrected] [--json]
    • If --corrected is passed, set view=corrected.
    • Default prints structured human-readable table with correction indicators.
    • --json emits standard JSON envelope.

Acceptance Criteria

  • Correcting a user report appends a new row to life_events with correction_target set to the target's event_id, while leaving the original row completely unmodified (verifying Scenario W14).
  • Attempting an SQL UPDATE or DELETE on life_events triggers an SQLite abort exception (life_events_no_update / life_events_no_delete).
  • Executing a timeline correction causes ZERO writes or mutations on Radicale CalDAV storage (Scenario W14).
  • Attempting to correct an event with evidence_class: "confirmed_service" fails with HTTP 400 and code: "invalid_input" (verifying Scenario R13).
  • Attempting to correct a non-existent event ID fails with HTTP 404 / 400 invalid_input (Scenario R13).
  • Attempting to introduce a cyclic correction reference (A \to B \to A) is detected and rejected with invalid_input (Scenario R13).
  • Running life timeline --view raw returns both the original and corrective events.
  • Running life timeline --corrected --json returns the latest folded values and explicitly discloses original event IDs and correction provenance (Scenario W14).
  • Timeline query distinguishes record-time filters (from/to) from occurrence-time filters (occurred_from/occurred_to), explicitly reporting unknown_time_count and coverage across paginated results (Scenario W15).

Notes

  • Corrections are bounded at MAX_CORRECTION_HOPS = 16 and cannot rewrite a confirmed_service transport fact; a replacement outside the correctable vocabulary is refused.
  • Raw and corrected views are two projections of the same append-only log; the original row and the correction chain remain visible.
  • #17's refusal fixture now reflects that kind: correction is implemented, and the skill's CLI reference was updated in the same commit.
  • No dependency added.

Blocked by

  • #028: Work-Block Attendance & Outcome Fact Capture
## Summary Implement append-only timeline corrections (`POST /v1/captures` with `kind: "correction"`, and `life timeline [--corrected]`), appending new corrective events into SQLite `life_events` referencing earlier events via `correction_target`, strictly enforcing immutable table triggers, rejecting attempts to rewrite confirmed transport facts or create cyclic references (`invalid_input`), and providing both raw chronological and folded corrected timeline query views. ## Background & Technical Rationale Human memory and initial reporting are fallible. Users will inevitably report inaccurate durations, misattribute work blocks, or record mistaken outcomes. In conventional systems, corrections are handled by performing destructive SQL `UPDATE` or `DELETE` operations on historical rows. This approach creates severe systemic failure modes: 1. **Destruction of Audit History:** Destructive updates destroy the record of what the user originally reported, corrupting provenance and making it impossible to reconstruct why an earlier plan or recommendation was made. 2. **Cache and Outbox Invalidation:** Background processors and external sync workers relying on immutable sequence numbers will encounter corrupted state or missing records. 3. **Falsification of Transport Realities:** Allowing a user correction to alter an acknowledged CalDAV transaction row (`evidence_class: "confirmed_service"`) falsifies the historical fact of what was actually transmitted to and acknowledged by the remote server. Life guarantees historical integrity through an append-only architecture: - **Strict Database Triggers:** SQLite table `life_events` is guarded by triggers `life_events_no_update` and `life_events_no_delete` that raise `ABORT`. No application code or administrator query can update or delete rows through normal operations. - **Corrections as New Linked Records:** A correction appends a NEW row to `life_events` where `correction_target` stores the `event_id` of the target record. The target record remains unaltered in storage (Scenario W14). - **Zero CalDAV Mutation:** Correcting a timeline event does NOT update Radicale CalDAV calendar components. Fixing live item state requires a separate conditional item mutation (e.g. `life task update`). - **Transport Fact Protection (Scenario R13):** Only user assertions (`evidence_class: "user_report"`) and derived interpretations (`evidence_class: "derived"`) are correctable. Transport confirmations (`evidence_class: "confirmed_service"`) represent verified physical network transactions and cannot be corrected via capture. - **Graph Invariants & Cycle Detection (Scenario R13):** The engine verifies that `correction_target` exists and performs cycle detection to prevent $A \to B \to A$ reference loops. Invalid targets or cycles are rejected with `invalid_input` (HTTP 400 / Exit 2). - **Dual Query Views:** Callers can query raw timeline facts (`--view raw`) or a folded corrected view (`--view corrected`) that computes the effective state by following correction chains while exposing original event IDs and provenance. ## Exact Technical References & Specifications ### 1. Timeline Consistency & Correction Semantics (`docs/technical/timeline-consistency.md`) Section: `## Context delivery and correction` (lines 160–165): ```markdown Correction appends a user assertion referencing correction_target; corrected views apply the latest valid correction chain in recorded sequence order and show provenance. Reject cycles, cross-type corrections and attempts to rewrite confirmed transport facts. Raw views retain originals. Append-only correction does not erase sensitive content; exceptional erasure and backup consequences belong to Operations. ``` Section: `## Illustrative schema` (lines 37–62): ```sql CREATE TABLE life_events ( sequence INTEGER PRIMARY KEY AUTOINCREMENT, event_id TEXT NOT NULL UNIQUE, dedupe_key TEXT NOT NULL UNIQUE, schema_version INTEGER NOT NULL, kind TEXT NOT NULL, evidence_class TEXT NOT NULL CHECK(evidence_class IN ('confirmed_service','observed_external','user_report','derived')), recorded_at TEXT NOT NULL, occurred_at TEXT, actor TEXT, source TEXT NOT NULL, collection_id TEXT, uid TEXT, recurrence_key TEXT, before_json TEXT, after_json TEXT, reason TEXT, provenance_json TEXT NOT NULL, operation_id TEXT REFERENCES operations(operation_id), correction_target TEXT REFERENCES life_events(event_id) ); CREATE TRIGGER life_events_no_update BEFORE UPDATE ON life_events BEGIN SELECT RAISE(ABORT, 'append-only events'); END; CREATE TRIGGER life_events_no_delete BEFORE DELETE ON life_events BEGIN SELECT RAISE(ABORT, 'append-only events'); END; ``` ### 2. Workflows Specification (`docs/technical/workflows.md`) Section: `## Lifecycle and review` (line 38): ```markdown | Workflow | Input and reads | Decision and approval | Writes and observable result | Failure path | |---|---|---|---|---| | Correction | Target timeline event, corrected assertion, reason and evidence | Explicit user assertion; distinguish fact correction from calendar mutation | Structured capture appends correction referencing original; corrected view retains provenance | Missing target rejected; correcting timeline alone never updates Radicale | ``` Section: `## Capture and retrieval` (line 20): ```markdown | Timeline query | Bounded record-time range, optional occurred-time range/item/evidence class | Query SQLite; corrections folded only when requested, originals remain available | Cursor-paginated facts with coverage and actual-time uncertainty | Missing observation interval is unknown, not zero activity | ``` Section: `## Weekly analytics` (line 56): ```markdown Correction-aware totals identify the correction policy and retain links to original evidence. ``` ### 3. CLI and API Route & Payloads (`docs/technical/cli-api.md`) Section: `## Command-to-route table` (lines 40, 48): ```markdown | `life capture --input -` | POST /v1/captures | write | | `life timeline` | GET /v1/timeline | read | ``` Section: `## Command payloads and results` (lines 87, 95, 102): ```markdown | Capture | kind task/event/context/outcome/correction; payload with corresponding fields; optional context for actionable capture | operation_id, per-part results and delivery state; partial parts remain explicit | | Timeline | Default last 7 local days by recorded_at; optional from/to,item_id,evidence_class,occurred_from/to,view raw/corrected | Paginated timeline events, coverage, unknown-time count; schema | Outcome capture payload: item_id, outcome (domain enum), optional occurred_at, actual_minutes, qualifies_career_floor (explicit user assertion), reason. Correction payload: target_event_id, replacement (same assertion type as target), reason. Only user assertions and derived interpretations may be corrected through capture; confirmed transport facts cannot be rewritten into different remote results. Fixing item state uses a separate conditional item mutation. ``` Section: `## Error identifiers` (line 110): ```markdown | invalid_input, configuration_invalid | Correct request/configuration before retry | ``` ### 4. Verification Scenarios W14 & R13 (`docs/technical/verification.md`) Section: `## Workflow scenarios` (line 28): ```markdown | ID | Requirement and specification | Input / action | Observable acceptance | |---|---|---|---| | W14 | Corrections (timeline-consistency.md#context-delivery-and-correction) | Correct a user report and query raw/corrected views | New linked event; original retained; corrected view provenance; Radicale unchanged | ``` Section: `## Recovery scenarios` (line 99): ```markdown | ID | Boundary / injected failure | Observable acceptance | |---|---|---| | R13 | Correction tries to rewrite confirmed transport fact or form cycle | invalid_input; append-only original remains intact | ``` ## What to build ### 1. Correction Validator & Cycle Detector (`src/domain/timeline/correction.rs`) - Validate correction payload: - Input JSON: ```json { "kind": "correction", "payload": { "target_event_id": "evt_out_01HN...", "replacement": { "outcome": "attended", "actual_minutes": 45, "qualifies_career_floor": true }, "reason": "Correcting previously misreported duration" } } ``` - Verify target event existence: - Query `life_events WHERE event_id = ?`. - If not found, return `404 Not Found` with `code: "not_found"`. - Verify evidence class: - If `target.evidence_class == "confirmed_service"`: - Reject immediately with `400 Bad Request` and `code: "invalid_input"` ("Cannot correct confirmed transport facts") (verifying Scenario R13). - Only allow correction if `target.evidence_class IN ('user_report', 'derived')`. - Verify semantic match: - The `replacement` JSON structure must match the semantic kind of `target.kind`. - Perform Cycle Detection: - Traverse existing correction links backwards: starting from `target_event_id`, follow `correction_target` references. - If the new correction would introduce a loop (e.g. $A \to B \to A$), reject immediately with `400 Bad Request` and `code: "invalid_input"` (verifying Scenario R13). ### 2. Timeline Correction Appender (`src/storage/timeline_repo.rs`) - Construct new `life_events` row: - `event_id`: Unique prefixed ID (`evt_cor_...`). - `dedupe_key`: Derived from `operation_id` (`op_cor_...:fact:correction`). - `schema_version`: `1`. - `kind`: `target.kind` (or `"correction"`). - `evidence_class`: `"user_report"`. - `recorded_at`: Current UTC timestamp. - `occurred_at`: `replacement.occurred_at` if provided, else `target.occurred_at`. - `actor`: `"user"`. - `source`: `"cli_api"`. - `collection_id`: `target.collection_id`. - `uid`: `target.uid`. - `before_json`: `target.after_json`. - `after_json`: Serialized `replacement` payload. - `reason`: The user-provided reason string. - `provenance_json`: Metadata including client request ID and `target_event_id`. - `operation_id`: Current operation ID. - `correction_target`: `target.event_id`. - Execute SQL insert into `life_events`. - Invariant: Zero writes or modifications to Radicale CalDAV storage (verifying Scenario W14). ### 3. Timeline Query Projection Engine (`src/domain/timeline/projection.rs`) - Implement `GET /v1/timeline`: - Query parameters: - `from`, `to`: ISO-8601 timestamps filtering by `recorded_at` (default: last 7 local days). - `occurred_from`, `occurred_to`: Optional ISO-8601 timestamps filtering strictly by `occurred_at`. - `view`: Enum `"raw"` | `"corrected"` (default: `"raw"`). - `item_id`: Optional filter. - `evidence_class`: Optional filter. - `cursor`: Opaque pagination cursor. - `limit`: Integer (default 50, max 200). - Response data envelope contains: - `events`: Paginated list of timeline items. - `next_cursor`: Null at end. - `coverage`: Temporal observation coverage metadata. - `unknown_time_count`: Count of returned events with unknown `occurred_at`. - If `view == "raw"`: - Return all `life_events` ordered by `sequence ASC` (or `recorded_at`). - Includes both original rows and corrective rows. - If `view == "corrected"`: - Fetch all relevant events in the query interval plus their correction chains. - Fold corrections: For any event targeted by one or more valid corrections, apply the latest correction in sequence order. - Replace the effective data with the latest correction's `after_json`. - Annotate the returned item with correction metadata: ```json { "event_id": "evt_cor_01HN8...", "original_event_id": "evt_out_01HN7...", "correction_chain_length": 1, "kind": "work_block_outcome", "evidence_class": "user_report", "data": { "outcome": "attended", "actual_minutes": 45, "qualifies_career_floor": true }, "recorded_at": "2026-09-23T12:00:00Z", "provenance": { "corrected_at": "2026-09-23T12:00:00Z", "reason": "Correcting previously misreported duration" } } ``` ### 4. CLI Presentation (`src/cli/timeline.rs`) - Implement CLI flags: `life timeline [--from ISO] [--to ISO] [--occurred-from ISO] [--occurred-to ISO] [--corrected] [--json]` - If `--corrected` is passed, set `view=corrected`. - Default prints structured human-readable table with correction indicators. - `--json` emits standard JSON envelope. ## Acceptance Criteria - [x] Correcting a user report appends a new row to `life_events` with `correction_target` set to the target's `event_id`, while leaving the original row completely unmodified (verifying Scenario W14). - [x] Attempting an SQL `UPDATE` or `DELETE` on `life_events` triggers an SQLite abort exception (`life_events_no_update` / `life_events_no_delete`). - [x] Executing a timeline correction causes ZERO writes or mutations on Radicale CalDAV storage (Scenario W14). - [x] Attempting to correct an event with `evidence_class: "confirmed_service"` fails with HTTP 400 and `code: "invalid_input"` (verifying Scenario R13). - [x] Attempting to correct a non-existent event ID fails with HTTP 404 / 400 `invalid_input` (Scenario R13). - [x] Attempting to introduce a cyclic correction reference ($A \to B \to A$) is detected and rejected with `invalid_input` (Scenario R13). - [x] Running `life timeline --view raw` returns both the original and corrective events. - [x] Running `life timeline --corrected --json` returns the latest folded values and explicitly discloses original event IDs and correction provenance (Scenario W14). - [x] Timeline query distinguishes record-time filters (`from`/`to`) from occurrence-time filters (`occurred_from`/`occurred_to`), explicitly reporting `unknown_time_count` and `coverage` across paginated results (Scenario W15). ## Notes - Corrections are bounded at `MAX_CORRECTION_HOPS = 16` and cannot rewrite a `confirmed_service` transport fact; a replacement outside the correctable vocabulary is refused. - Raw and corrected views are two projections of the same append-only log; the original row and the correction chain remain visible. - `#17`'s refusal fixture now reflects that `kind: correction` is implemented, and the skill's CLI reference was updated in the same commit. - No dependency added. ## Blocked by - #028: Work-Block Attendance & Outcome Fact Capture
yuris changed title from Issue #029: Append-Only Timeline Fact Corrections to Append-Only Timeline Fact Corrections 2026-09-22 20:02:34 +00:00
yuris closed this issue 2026-09-23 03:58:59 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#29
No description provided.