# Story 10.18: Documentation Accuracy - Redaction and Envelope

**Status:** Complete
**Priority:** Critical
**Depends on:** None (immediate fix required)

---

## Context / Background

An external audit identified material misrepresentations in fapilog documentation that could lead users to build integrations and compliance expectations on incorrect assumptions.

**Finding 1: Redaction Guarantees Overstate Default Behavior**

| Documentation Claim | Actual Behavior |
|---------------------|-----------------|
| `docs/redaction-guarantees.md:7`: "URL credentials are **always** scrubbed from string values" | Redaction is **disabled by default**. Only enabled with `preset="production"` or explicit configuration. |
| `docs/stories/1.18.redaction-guarantees-and-validation.md:14-15`: "Always scrub URL credentials" | Same - not enabled by default |

**Evidence:**
- `docs/user-guide/reliability-defaults.md:24`: "With no preset: Redaction is **disabled** by default"
- `src/fapilog/core/presets.py:32`: Only `production` preset includes `"redactors": ["field_mask", "regex_mask", "url_credentials"]`
- `dev`, `fastapi`, `minimal` presets have NO redactors

**Impact:** Users may believe their logs are automatically scrubbed and expose credentials in production.

---

**Finding 2: Envelope Documentation Misrepresents Format**

| Documentation Claim | Actual Behavior |
|---------------------|-----------------|
| `docs/core-concepts/envelope.md`: `"timestamp": "2024-01-01T12:00:00.000Z"` (ISO 8601 string) | `build_envelope()` produces float seconds like `1704110400.123` |
| `docs/core-concepts/envelope.md`: "timestamp: ISO 8601 with milliseconds in UTC" | Actual output uses POSIX float format |
| `docs/core-concepts/envelope.md`: "metadata keys merged at top level" | No flattening - metadata stays nested under `"metadata"` key |

**Technical Note:** The serialization module has `ensure_rfc3339_utc()` that would normalize timestamps to RFC3339 if `serialize_envelope()` succeeded. However, due to the schema mismatch between `build_envelope()` and `serialize_envelope()` (addressed in Stories 1.26-1.28), sinks fall back to `serialize_mapping_to_json_bytes()` which preserves the float format. The net result for users is float timestamps.

**Note:** Story 1.26 will change timestamp to RFC3339 string as part of the unified schema fix.

**Impact:** Users building log parsers, integrations, or compliance reports on documented format will fail.

---

## Scope (In / Out)

### In Scope

- Fix `docs/redaction-guarantees.md` to accurately describe default behavior
- Fix `docs/core-concepts/envelope.md` to match actual output format
- Add clear "Defaults" or "Warning" callouts to critical documentation
- Update story 1.18 language if needed
- Add CI check to prevent future doc/code drift for critical claims

### Out of Scope

- Changing default redaction behavior (Story 3.7)
- Changing timestamp format (Story 1.26)
- Adding metadata flattening feature

---

## Acceptance Criteria

### AC1: Redaction Guarantees Document Accuracy

**Description:** `docs/redaction-guarantees.md` accurately describes when redaction is active.

**Before (misleading):**
```markdown
- URL credentials are always scrubbed from string values resembling `scheme://user:pass@host...` across all fields.
```

**After (accurate):**
```markdown
> **Important:** Redaction is disabled by default. To enable, use `preset="production"` or explicitly configure redactors.

- URL credentials are scrubbed from string values resembling `scheme://user:pass@host...` across all fields **when the `url_credentials` redactor is enabled**.
```

**Validation:**
- Document no longer uses "always" without qualification
- Clear callout box at top explains default behavior
- Links to preset documentation for enabling redaction

### AC2: Envelope Document Timestamp Accuracy

**Description:** `docs/core-concepts/envelope.md` accurately describes current timestamp format.

**Before (misleading):**
```markdown
"timestamp": "2024-01-01T12:00:00.000Z",
...
- `timestamp`: ISO 8601 with milliseconds in UTC.
```

**After (accurate):**
```markdown
"timestamp": 1704110400.123,
...
- `timestamp`: POSIX timestamp as float seconds (UTC).
  > **Note:** Story 1.26 will change this to RFC3339 string format (`"2024-01-01T12:00:00.000Z"`) in a future release.
```

**Validation:**
- JSON example shows actual float format
- Description matches code behavior
- Note indicates planned change

### AC3: Envelope Document Metadata Accuracy

**Description:** Remove false claim about metadata flattening.

**Before (false):**
```markdown
- Default stdout sink emits JSON lines with these fields flattened (metadata keys merged at top level).
```

**After (accurate):**
```markdown
- Default stdout sink emits JSON lines preserving the envelope structure. The `metadata` field remains nested.
```

**Validation:**
- No claim of flattening (which doesn't exist)
- Accurately describes nested structure

### AC4: Reliability Defaults Cross-Reference

**Description:** `docs/redaction-guarantees.md` prominently links to reliability defaults documentation.

**Validation:**
- First paragraph or callout box references `docs/user-guide/reliability-defaults.md`
- Users can quickly understand what's enabled by default

### AC5: Story 1.18 Language Updated

**Description:** If story 1.18 is referenced as authoritative, update its language to match corrected guarantees.

**Validation:**
- `docs/stories/1.18.redaction-guarantees-and-validation.md:14-15` updated
- "Always scrub" becomes "Scrub (when enabled)" or similar

### AC6: Documentation Accuracy CI Check

**Description:** Add a CI check that validates critical documentation claims against code behavior.

**Validation:**
```yaml
# In CI workflow
- name: Validate doc claims
  run: python scripts/check_doc_accuracy.py
```

Script checks:
- Redaction guarantees doc mentions "disabled by default" or "when enabled"
- Envelope doc timestamp example is float (until 1.26 merges)
- No "always" claims without qualification for optional features

---

## Implementation Notes

### File Changes

```
docs/redaction-guarantees.md (MODIFIED - add accuracy callouts)
docs/user-guide/redaction-guarantee.md (MODIFIED - add accuracy callouts)
docs/core-concepts/envelope.md (MODIFIED - fix timestamp and metadata claims)
docs/user-guide/reliability-defaults.md (MODIFIED - add cross-reference if needed)
docs/stories/1.18.redaction-guarantees-and-validation.md (MODIFIED - update language)
scripts/check_doc_accuracy.py (NEW - CI validation script)
.github/workflows/ci.yml (MODIFIED - add doc accuracy check)
```

### Callout Box Format

Use consistent callout format for important caveats:

```markdown
> **Important:** [Key caveat about default behavior]
```

Or with admonition syntax if supported:

```markdown
!!! warning "Default Behavior"
    Redaction is disabled by default. See [Reliability Defaults](../user-guide/reliability-defaults.md) for details.
```

---

## Tasks

### Phase 1: Redaction Documentation

- [ ] Add "disabled by default" callout to `redaction-guarantees.md`
- [ ] Change "always scrubbed" to "scrubbed when enabled"
- [ ] Add link to reliability-defaults.md
- [ ] Update story 1.18 language

### Phase 2: Envelope Documentation

- [ ] Change timestamp example from string to float
- [ ] Update timestamp description with note about Story 1.26
- [ ] Remove metadata flattening claim
- [ ] Describe actual nested structure

### Phase 3: CI Enforcement

- [ ] Create `scripts/check_doc_accuracy.py`
- [ ] Add patterns to detect misleading language
- [ ] Add CI workflow step
- [ ] Document the check in CONTRIBUTING.md

---

## Tests

### CI Validation Script

`scripts/check_doc_accuracy.py`:
```python
"""Validate documentation accuracy for critical claims."""

import re
import sys
from pathlib import Path

CHECKS = [
    {
        "file": "docs/redaction-guarantees.md",
        "must_contain": ["disabled by default", "when enabled", "preset"],
        "must_not_contain": [r"always scrubbed(?! when)", r"always redact(?!ed when)"],
    },
    {
        "file": "docs/user-guide/redaction-guarantee.md",
        "must_contain": ["disabled by default", "when enabled"],
        "must_not_contain": [r"always scrubbed(?! when)", r"always redact(?!ed when)"],
    },
    {
        "file": "docs/core-concepts/envelope.md",
        "must_not_contain": [r"flattened.*metadata", r"merged at top level"],
    },
]

def check_file(check):
    path = Path(check["file"])
    if not path.exists():
        print(f"SKIP: {path} not found")
        return True

    content = path.read_text()
    errors = []

    for pattern in check.get("must_contain", []):
        if pattern.lower() not in content.lower():
            errors.append(f"Missing required text: '{pattern}'")

    for pattern in check.get("must_not_contain", []):
        if re.search(pattern, content, re.IGNORECASE):
            errors.append(f"Found prohibited pattern: '{pattern}'")

    if errors:
        print(f"FAIL: {path}")
        for e in errors:
            print(f"  - {e}")
        return False

    print(f"PASS: {path}")
    return True

if __name__ == "__main__":
    results = [check_file(c) for c in CHECKS]
    sys.exit(0 if all(results) else 1)
```

---

## Definition of Done

### Code Complete

- [ ] All misleading documentation corrected
- [ ] Callout boxes added for critical caveats
- [ ] Cross-references to defaults documentation
- [ ] CI check implemented and passing

### Quality Assurance

- [ ] Documentation renders correctly
- [ ] Links work
- [ ] CI check catches the original issues (test by reverting temporarily)
- [ ] No regression in doc build

### Documentation

- [ ] CHANGELOG notes documentation corrections
- [ ] CONTRIBUTING.md mentions doc accuracy check

---

## Risks / Rollback

### Risks

1. **Risk:** Users relied on misleading docs and now feel misled
   - **Mitigation:** Clear changelog entry explaining correction; Story 3.7 addresses secure defaults

2. **Risk:** CI check too strict, blocks valid documentation
   - **Mitigation:** Patterns are specific; easy to adjust

### Rollback Plan

Documentation changes are low-risk:
1. Revert the commit if needed
2. CI check can be disabled temporarily

---

## Related Stories

- **Enables:** Story 3.7 - Secure Defaults Enhancement (users can understand current state before changes)
- **Related:** Story 1.26 - Canonical Log Schema v1.1 (will fix timestamp format)
- **Related:** Story 10.15 - Documentation Correctness CI Enforcement (similar pattern)

---

## Code Review

**Date:** 2026-01-19
**Reviewer:** Claude
**Verdict:** OK to PR

### Summary

Reviewed documentation fixes for redaction defaults and envelope format accuracy, plus new CI check script.

### AC Verification

| Criterion | Evidence |
|-----------|----------|
| AC1: Redaction Guarantees Accuracy | `docs/redaction-guarantees.md:5,9-11` - Callout added, "always" changed to "when enabled" |
| AC2: Envelope Timestamp Accuracy | `docs/core-concepts/envelope.md:16,28-29` - Float format with Story 1.26 note |
| AC3: Envelope Metadata Accuracy | `docs/core-concepts/envelope.md:57` - Nested structure, no flattening claim |
| AC4: Reliability Defaults Cross-Reference | `docs/redaction-guarantees.md:5` - Links to reliability-defaults.md |
| AC5: Story 1.18 Language Updated | `docs/stories/1.18.redaction-guarantees-and-validation.md:14-15` - "when enabled" |
| AC6: CI Check Implemented | `scripts/check_doc_accuracy.py`, `.github/workflows/ci.yml:236-237` |

### Quality Gates

- [x] ruff check passed
- [x] ruff format passed
- [x] mypy passed
- [x] Coverage 100% on new script
- [x] No weak assertions
- [x] Doc accuracy check passes

---

## Change Log

| Date | Change | Author |
|------|--------|--------|
| 2025-01-19 | Initial draft based on audit findings | Claude |
| 2025-01-19 | Added technical note on timestamp behavior; added docs/user-guide/redaction-guarantee.md to scope | Claude |
| 2026-01-19 | Implementation complete, code review passed | Claude |
