W3.2 (source line ranges per chunk):
- common.MarkdownChunk dataclass + chunk_markdown_with_lines() tracks
1-indexed source line ranges as it groups paragraphs into chunks.
Old chunk_markdown() preserved as a thin wrapper for callers that
don't need line ranges.
- ingest_artifact.build_chunks_for_document now emits
source_start_line / source_end_line on every chunk.
- knowledge-chunk schema accepts the two new fields (nullable for
backward compat with pre-W3.2 chunks; required for new ones).
- context_pack.PackSection carries the same fields; module-map
sections record start=1 / end=line_count of the full file. Manifest
output and pack.md citation grammar are unchanged.
- validate_citations.verify() for kind=file_line now searches the
pack for sections whose source_path matches AND whose line range
overlaps the citation. On hit, inherits that section's tier
(typically ssot for module.yml/ADR sections). On no overlap,
falls back to the existing weak verification (bounds-only).
Smoke (against ljwx-platform with module-map):
- spec/modules/license/module.yml:L1-L14 -> tier=ssot, severity=block
- docs/adr/006-security-hardening.md:L40-L60 -> tier=ssot, severity=block
- :L200-L300 (out of bounds) -> unverified, info
- nonexistent/path.md -> unverified, info
W4.5 (Dify drift check):
- new scripts/knowledge/dify_drift_check.py: read-only sweep that for
each published record verifies (1) the dataset still exists with the
recorded dataset_id, (2) the document still exists with the recorded
remote_document_id, (3) the document name on Dify matches.
- Emits per-record + per-repo JSON report; exits non-zero with
--fail-on-drift for CI.
- Smoke against ljwx-platform: 0 drift in 10-record sample.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>