Six fixes for the c11_python doc sync (chronology row 3):
- C5 (Result notation): Result[str, ErrorInfo] -> Result[str] at
docs/guide_ai_client.md lines 452 + 469; also error_handling.md
line 801 (historical deprecation section).
- C6 (RAGChunk schema): docs/guide_models.md lines 343-349 corrected
to match src/rag_engine.py:19-25 (id, document, path, score, metadata).
- C17 (type_aliases.md table): rewrote alias table to reflect post-2026-06-25
reality (Metadata is @dataclass(frozen=True, slots=True) with 36 fields;
11 per-aggregate dataclasses listed with source locations; removed
stale 'underlying type is dict[str, Any]' claim at line 73 + the
'keep Metadata as dict[str, Any]' claim at line 81).
- C19 (OBLITERATE principle): added 'OBLITERATE Principle' section to
error_handling.md after Migration Playbook; clarified in Hard Rules
that argument types that may be None (caller choice) are NOT banned.
- C2 (audit script name): docs/AGENTS.md references updated to point
to scripts/audit_optional_returns.py (the all-src/ successor to
scripts/audit_optional_in_3_files.py).
Also: docs/reports/CONTRADICTIONS_REPORT_20260627.md — the contradictions
index that drives these fixes. Kept for reference.
C16 + C18 were already addressed in commit 770c2fdb (python.md §10
Documented Exceptions table + §17.10 audit inventory).
22 KiB
Documentation Contradictions Report — 2026-06-27
Scope: All agent-directive markdowns (AGENTS.md, conductor/*.md, conductor/code_styleguides/*.md, docs/*.md) cross-referenced for logical soundness.
Method: Read all 14 styleguides + all 8 conductor root files + all 38 docs/*.md files end-to-end, then grep'd/selected specific claims against src/*.py and scripts/*.py to verify code-state alignment.
Total contradictions found: 21 across 8 categories.
Severity Legend
| Level | Meaning |
|---|---|
| 🔴 CRITICAL | Misleads agents into violating a Core Value mandate or running broken code |
| 🟠 HIGH | Contradicts an active spec/plan or causes agents to make wrong decisions |
| 🟡 MEDIUM | Drift between doc and code; mostly harmless but creates noise |
| 🟢 LOW | Doc tidiness; doesn't change agent behavior |
Category 1: Mandatory Convention Enforcement Gaps 🔴🟠
These are the highest-impact contradictions: they make the Core Value mandate (2026-06-25) appear enforceable when it isn't.
C1 — Optional[T] audit script name vs behavior 🟠
Claim: conductor/code_styleguides/error_handling.md:212 says "Hard Rules (enforced in the 3 refactored files)". docs/AGENTS.md §"Convention Enforcement" says audit scripts run pre-commit. error_handling.md:885 says the rule applies to "the 3 refactored files".
Reality:
scripts/audit_optional_in_3_files.py:24-29definesBASELINE_FILES = ("src/mcp_client.py", "src/ai_client.py", "src/rag_engine.py", "src/code_path_audit.py")— 4 files, not 3.- The script is named
audit_optional_in_3_files.pybut covers 4. Internal contradiction between filename and behavior. - The script has not been "extended to all
src/*.pyper the c11_python track" asdocs/AGENTS.mdclaims.
Fix: Rename to audit_optional_in_baseline_files.py AND either (a) update BASELINE_FILES to actually be all src/*.py OR (b) update the docs to accurately reflect that the enforcement is only on 4 baseline files. The cruft_elimination_20260627 spec says all 14 migration-target files should also be migrated, but there's no enforcement.
C2 — Optional[T] ban scope ambiguity in docs 🟠
Claim 1: conductor/code_styleguides/error_handling.md:212-222 says "Optional[T] return types are FORBIDDEN in the 3 refactored files" (mcp_client, ai_client, rag_engine).
Claim 2: docs/AGENTS.md §"Convention Enforcement" says "scripts/audit_optional_in_3_files.py --strict (extended to all src/*.py per the c11_python track)".
Claim 3: conductor/tracks/cruft_elimination_20260627/state.toml:18 says Phase 6 (Optional[T] returns, 30 sites across 14 files) is "deferred".
Contradiction: The docs claim enforcement "extended to all src/*.py", but the audit script still only checks 4 files. The cruft_elimination_20260627 spec says 30 sites remain across 14 untracked files — those are NOT enforced. An agent reading the docs would think the rule is global; in practice it's only enforced on 4 files.
Fix: Either (a) actually extend the audit script + rename it OR (b) clarify the docs: ban is enforced on baseline 4 files; cruft_elimination is the migration track for the remaining 14.
C3 — Banned-pattern audit script "planned" but never built 🟠
Claim: conductor/code_styleguides/python.md:413 says "The static analysis script scripts/audit_imports.py (planned) flags local imports outside try/except ImportError blocks."
Reality: scripts/audit_imports.py does NOT exist (verified via ls scripts/audit_imports.py). The 7-banned-pattern mandate has only 4 enforcement scripts (audit_weak_types, audit_optional_in_3_files, audit_exception_handling, generate_type_registry), not 5.
Fix: Either (a) build the script OR (b) remove the "planned" reference from python.md. The mandate has a gap: local imports + _PREFIX aliasing are policy without enforcement.
C4 — Tier 2 pre-commit enforcement is sandbox-only 🟡
Claim: docs/AGENTS.md §"The pre-commit workflow" says "run before claiming 'done': uv run python scripts/audit_*.py [...] In CI / pre-commit hook" — implying pre-commit hooks exist.
Reality: Only conductor/tier2/githooks/pre-commit exists (per tier2_leak_prevention_20260620). There is no pre-commit hook in the main repo's .git/hooks/. The 4 audits listed are only enforced inside the Tier 2 sandbox.
Fix: Either (a) install the audits as actual pre-commit hooks in the main repo OR (b) clarify that the convention is enforced in Tier 2 sandbox only; the main repo relies on agent discipline + manual runs.
Category 2: Doc vs Code State Drift 🟠🟡
C5 — Result[T, ErrorInfo] notation is wrong 🟠
Claim: docs/guide_ai_client.md:452 says all 8 vendors "return Result[str, ErrorInfo]". Same file line 469 says ai_client.send(...) returns "Result[str, ErrorInfo]".
Reality: conductor/code_styleguides/error_handling.md:91 defines:
class Result(Generic[T]):
data: T
errors: list[ErrorInfo] = field(default_factory=list)
The signature is Result[T] (generic over success type only). Errors is a FIELD, not a type parameter. Correct notation is Result[str] (where .errors: list[ErrorInfo] is always the shape).
Fix: Replace all Result[str, ErrorInfo] in guide_ai_client.md with Result[str] (and reference the field .errors: list[ErrorInfo] separately). Same fix in any other guide that uses this notation.
C6 — RAGChunk schema is stale in guide_rag.md 🟠
Claim: docs/guide_rag.md:343-350 documents RAGChunk fields as text, source_path, start_line, end_line, embedding.
Reality: src/rag_engine.py:20-21 defines RAGChunk with an additional id: str = "" field, added per cruft_elimination_20260627 Phase 5 ("Added id: str field to RAGChunk dataclass"). The guide does not show this field.
Fix: Update guide_rag.md:343-350 to include the id: str = "" field. Also update docs/guide_models.md RAGChunk dataclass section to include id.
C7 — Provider count: Readme.md says 5, guide says 8 🟠
Claim 1: docs/Readme.md:34 says guide_ai_client.md covers "multi-provider LLM singleton (5 providers: Gemini, Anthropic, DeepSeek, MiniMax, Gemini CLI)".
Claim 2: docs/guide_ai_client.md:9-10 says "The module is a unified LLM client for 8 providers. It abstracts the differences between providers (Gemini, Anthropic, DeepSeek, MiniMax, Gemini CLI, Qwen, Grok, Llama) ... The OpenAI-compatible vendors all call the shared helper in src/openai_compatible.py".
Fix: Update docs/Readme.md:34 to say "8 providers" (matching the actual codebase).
C8 — Test count: Readme.md says 322, guide says 251 🟠
Claim 1: docs/Readme.md:31 says "322 test files". Same file line 365 says "guide_testing.md # 322 test files".
Claim 2: docs/guide_testing.md:9 says "Manual Slop has 251 test files". Same file line 26 says "test_*.py # 251 test files".
Reality: The codebase has 251 test files; the Readme is stale (the 322 number likely came from a time when _sim.py files were double-counted, or included the _e2e.py files).
Fix: Update docs/Readme.md:31, 365 to "251 test files".
C9 — Command count: Readme.md says 50+, guide says 33 🟠
Claim 1: docs/Readme.md:30 says "Command Palette ... 50+ built-in commands".
Claim 2: docs/guide_command_palette.md:196 says "The 33 commands currently shipped in src/commands.py". Same file line 4 says "33 registered commands".
Fix: Update docs/Readme.md:30 to "33 built-in commands".
C10 — metadata_promotion_20260624 was supposed to add 12 dataclasses; 11 went to type_aliases.py + 1 to rag_engine.py 🟡
Claim: conductor/chronology.md:4 (the canonical index): "add 12 per-aggregate @dataclass(frozen=True) classes (CommsLogEntry, HistoryMessage, FileItem, ToolDefinition, RAGChunk, SessionInsights, DiscussionSettings, CustomSlice, MMAUsageStats, ProviderPayload, UIPanelConfig, PathInfo)".
Reality: The 12 includes RAGChunk, but RAGChunk was actually placed in src/rag_engine.py:20-21, not in src/type_aliases.py. The other 11 went to type_aliases.py (some with from_dict(), some not). So the spec said "12 in type_aliases.py" but the implementation put 11 in type_aliases.py + 1 in rag_engine.py.
Fix: Update conductor/chronology.md:4 to clarify the location split. Update conductor/tracks/metadata_promotion_20260624/spec.md G3 to reflect the actual implementation.
Category 3: Status Drift in tracks.md and chronology.md 🟠
The "active queue" in tracks.md does not match what chronology.md says is shipped.
C11 — live_gui_test_fixes_20260618 shipped but tracks.md says "active" 🟠
Claim: conductor/tracks.md row 7d shows live_gui_test_fixes_20260618 with status "active" (in the "Active Tracks (Current Queue)" table).
Reality: conductor/chronology.md:12 says the track is "Completed" with ff40138f..6ce55cba (2) commits.
Fix: Move row 7d out of the Active Tracks table and into the appropriate Phase section (or mark as shipped with link to TRACK_COMPLETION).
C12 — test_sandbox_hardening_20260619 shipped but tracks.md says "ready to start" 🟠
Claim: conductor/tracks.md row 16 shows test_sandbox_hardening_20260619 with status "ready to start".
Reality: conductor/chronology.md:11 says "Completed" with ec0716c9..eec44a09 (9) commits. TRACK_COMPLETION_test_sandbox_hardening_20260619.md exists at the documented path. tracks.md row 16 also has 16 | A | Test Sandbox Hardening listed in the active queue.
Fix: Mark as shipped; move to Phase section; link to TRACK_COMPLETION_test_sandbox_hardening_20260619.md.
C13 — metadata_promotion_20260624 listed as active but honest state is Phase 1 done + Phases 2-10 NO-OP 🟠
Claim 1: conductor/tracks.md (per my earlier read; full text was truncated) shows the track.
Claim 2 (honest): conductor/chronology.md:4 says: "Tier 2 added the dataclasses (with drifted field types vs the plan), completed Phase 1 (Ticket migration), but classified Phases 2-10 as no-op per FR2. State on branch: lied about completion (status = 'completed' with all phases 'completed (no-op per audit)'). Tier 1 followup corrected to honest state (status = 'active', current_phase = 0)."
Contradiction: The track is labeled "active at phase 0" but Phase 1 was completed and shipped. The "no-op" classification of Phases 2-10 means the rest of the work is "documented as deferred" not "to do". An agent reading the active queue would think this is a track to start; in reality it's a track where Phase 1 is done and the rest is filed as a no-op.
Fix: Move metadata_promotion_20260624 to a "completed Phase 1; Phases 2-10 classified NO-OP" status. Either complete the parent track (the work is done) or rename the state to reflect "1/10 phases done; remaining deferred" so agents don't pick it up.
C14 — result_migration_20260616 parent and sub-track status drift 🟡
Claim 1: conductor/tracks.md row 6 (per my earlier read) shows result_migration_20260616 as "active".
Claim 2: conductor/chronology.md:6 shows result_migration_baseline_cleanup_20260620 as "active". But docs/reports/RESULT_MIGRATION_CAMPAIGN_STATUS_20260619.md (updated by Phase 9 patch 2026-06-21) says the campaign is closed.
Contradiction: The 5-sub-track campaign (result_migration_20260616 with sub-tracks 6d-1 through 6d-6) is 100% complete per the close-out report. But tracks.md and chronology.md still show "active".
Fix: Update the parent track state to "closed" or "completed" with link to the campaign close-out. Same for sub-track 6 (baseline_cleanup).
C15 — result_migration_baseline_cleanup_20260620 status in tracks.md 🟡
Claim: conductor/chronology.md:6 shows result_migration_baseline_cleanup_20260620 as "active". Per TRACK_COMPLETION_result_migration_cruft_removal_20260620.md, the campaign closed 2026-06-20 with Phase 9 patch 2026-06-21.
Fix: Mark as shipped/closed.
Category 4: Internal Styleguide Contradictions 🟠🟡
C16 — python.md §10 Anti-OOP rule vs actual codebase 🟠
Claim: conductor/code_styleguides/python.md:73-110 says "Anti-OOP Conventions" + "Hard Rules (Enforced by lint)" — "Never write a class for a single method. Use a function." "Never use inheritance for code reuse. Compose with standalone functions." "Never use private methods (_method). Module-level functions with clear names suffice." "No nested classes. Define helper types at module level." "No decorator classes."
Justification rule (python.md:87-101): "A class is justified ONLY when ALL of: 1. It holds mutable state that must be encapsulated. 2. It has 3+ related methods that share state. 3. It implements a behavioral interface used polymorphically (not just data grouping)."
Self-contradiction (python.md:203-205): "Removed anti-pattern (2026-06-11): the prior version of this section said 'extremely large files that violate the Anti-OOP rule by necessity.' ... The App class in src/gui_2.py is not 'violating' anything by being large; it's the natural shape of a class that owns the GUI orchestration."
Reality: The codebase has App (150+ methods), AppController (166KB), ConductorEngine, WorkerPool, RAGEngine, MultiAgentConductor, etc. — all stateful classes. App does NOT satisfy criterion #3 (used polymorphically — it's a singleton). So App and AppController would fail the §10.4 rule.
Contradiction within the SAME FILE: §10.1-§10.3 (strict bans) + §10.4 (3 criteria) + §203 (admission that the rule doesn't apply to App).
Fix: Rewrite §10 to clarify:
- §10.1: "Module-level functions for stateless logic (default)."
- §10.2: "Classes are justified for stateful subsystems (App, AppController, ConductorEngine, RAGEngine, etc.). The 3 criteria are: holds state + 3+ methods sharing state + used as a singleton OR has a behavioral interface." — drop criterion #3 OR reword as "or is instantiated as a stateful subsystem singleton."
- §10.5 (new): "Examples of justified classes in this codebase:
App(150+ methods, 90 delegation targets, holds the GUI state),AppController(the headless state container),ConductorEngine(orchestration state machine),WorkerPool(thread/semaphore state)."
C17 — type_aliases.md line 19 table contradicts its own body 🟠
Claim (line 19): "Metadata | dict[str, Any] | The root alias; any key-value record"
Claim (line 42): "UPDATED 2026-06-25 (the C11/Odin/Jai-in-Python mandate). Metadata is the typed fat struct at the wire boundary. It is @dataclass(frozen=True, slots=True) with explicit fields..."
Contradiction within the SAME FILE: The table at line 19 says Metadata is dict[str, Any]. The body at line 42 says it's a typed dataclass. The table was NOT updated when the body was rewritten.
Claim (line 73): "The underlying type is still dict[str, Any]; the alias name is the documentation."
Claim (line 81): "When NOT to promote: ... they keep Metadata: TypeAlias = dict[str, Any] as the catch-all."
Claim (line 59-61): "Metadata is NOT TypeAlias = dict[str, Any]. It is a typed fat struct. ... Anti-pattern (banned): Metadata: TypeAlias = dict[str, Any] (the lazy-typing escape hatch)."
Internal contradiction: Lines 19, 73, 81 say Metadata IS dict[str, Any]. Lines 42, 59-61 say it IS NOT. Lines 73 says "underlying type is still dict[str, Any]" — which means the aliases (CommsLogEntry = Metadata etc.) are all still dicts. But line 75-77 introduces per-aggregate dataclasses which contradict this.
Fix: Rewrite the table at line 13-34 to reflect post-2026-06-25 reality:
- Line 19 table:
Metadata|@dataclass(frozen=True, slots=True)(36 fields) | The boundary type at TOML/JSON wire - Line 24 table:
FileItem|@dataclass(frozen=True)| A single file in the context - Etc. — each per-aggregate alias should now point to its own dataclass, not to
Metadata - Line 73: REMOVE the "underlying type is still dict[str, Any]" claim
- Line 81: REMOVE the "keep
Metadata: TypeAlias = dict[str, Any]as the catch-all" —MetadataIS a dataclass now
C18 — python.md says banned but doesn't have lint enforcement for 3 of 7 banned patterns 🟡
Claim: conductor/code_styleguides/python.md:402-413 says:
- Line 403:
scripts/audit_weak_types.py --strict— flagsdict[str, Any],Any, anonymous tuple returns ✅ EXISTS - Line 407:
scripts/audit_optional_in_3_files.py --strict— flagsOptional[T]in the 3 refactored files ✅ EXISTS (but named wrong, see C1) - The boundary-layer audit — planned in
conductor/tracks/cruft_elimination_20260627/spec.md❌ NOT BUILT - Line 413:
scripts/audit_imports.py (planned)— flags local imports outsidetry/except ImportErrorblocks ❌ NOT BUILT
Reality: 7 banned patterns, only 2 have audit scripts. The boundary-layer audit and audit_imports are "planned" not "implemented".
Fix: Either build the missing audits OR explicitly mark them as "to-be-implemented, currently unenforced" so agents know what to actually check.
Category 5: Result Migration Campaign Docs 🟡
C19 — The 9 legacy Result[T] wrapper obliteration is documented but not in styleguide 🟡
Claim: conductor/tracks/result_migration_cruft_removal_20260620/spec.md documents the "OBLITERATE principle: no pass-throughs; no backward compat; in-site callers rewritten to use _x_result(...).ok directly; the dead code dies." This is a specific pattern that's enforced in the cleanup but isn't in conductor/code_styleguides/.
Fix: Add a "Result migration anti-patterns" section to error_handling.md documenting the OBLITERATE principle (when a function is migrated to Result, the legacy wrapper should be deleted; callers must be migrated in the same commit).
Category 6: cruft_elimination_20260627 state docs 🟡
C20 — Phase 7 ("60 Any params + 11 dict[str, Any]") numbers don't match audit_weak_types.py baseline 🟡
Claim 1 (spec): conductor/tracks/cruft_elimination_20260627/spec.md G4 says "Zero Any parameter types in internal code. Same grep with : Any returns 0" — target is 60 sites removed.
Claim 2 (audit baseline): Per boundary_layer_20260628.md and the audit baseline, there are 60 Any params + 11 dict[str, Any] params in the migration-target 14 files (post-refactor). The audit_weak_types.baseline.json records the post-refactor count.
Reality: The audit_weak_types.py --strict checks against the baseline JSON. The baseline count must be the same as the spec's target. If the spec says "60 Any sites" but the audit baseline is higher, the spec is wrong. If the baseline is the same, the spec is consistent.
Fix: Reconcile cruft_elimination_20260627/spec.md G3 + G4 + audit_weak_types.baseline.json numbers. Add a line "Baseline at start of Phase 7: 60 Any + 11 dict[str, Any]" with the exact JSON reference.
Category 7: Naming and Misc 🟢
C21 — audit_optional_in_3_files.py checks 4 files 🟢
Claim: Filename says "3 files". BASELINE_FILES defines 4 files (mcp_client, ai_client, rag_engine, code_path_audit).
Fix: Rename to audit_optional_in_baseline_files.py (see C1).
Summary Table
| # | Contradiction | Severity | Affected Files |
|---|---|---|---|
| C1 | audit_optional_in_3_files.py covers 4 files |
🟠 | python.md, error_handling.md, docs/AGENTS.md |
| C2 | Optional[T] ban scope ambiguity | 🟠 | error_handling.md, docs/AGENTS.md |
| C3 | audit_imports.py "planned" but never built |
🟠 | python.md |
| C4 | Pre-commit hooks only in Tier 2 sandbox | 🟡 | docs/AGENTS.md |
| C5 | Result[str, ErrorInfo] notation wrong |
🟠 | guide_ai_client.md |
| C6 | RAGChunk schema missing id: str field |
🟠 | guide_rag.md, guide_models.md |
| C7 | Provider count: Readme 5 vs guide 8 | 🟠 | docs/Readme.md |
| C8 | Test count: Readme 322 vs guide 251 | 🟠 | docs/Readme.md |
| C9 | Command count: Readme 50+ vs guide 33 | 🟠 | docs/Readme.md |
| C10 | 12 dataclasses location split | 🟡 | chronology.md, metadata_promotion_20260624/spec.md |
| C11 | live_gui_test_fixes_20260618 "active" but shipped |
🟠 | tracks.md |
| C12 | test_sandbox_hardening_20260619 "ready to start" but shipped |
🟠 | tracks.md |
| C13 | metadata_promotion_20260624 status confusion |
🟠 | tracks.md, chronology.md |
| C14 | result_migration_20260616 parent stale |
🟡 | tracks.md |
| C15 | result_migration_baseline_cleanup_20260620 stale |
🟡 | tracks.md, chronology.md |
| C16 | python.md §10 Anti-OOP vs App+AppController |
🟠 | python.md |
| C17 | type_aliases.md line 19 table vs body |
🟠 | type_aliases.md |
| C18 | 2/7 banned patterns have audit scripts | 🟡 | python.md |
| C19 | OBLITERATE principle not in styleguide | 🟡 | error_handling.md |
| C20 | cruft_elimination Phase 7 numbers vs baseline | 🟡 | cruft_elimination_20260627/spec.md |
| C21 | audit_optional_in_3_files.py checks 4 |
🟢 | script filename |
Recommended Fix Priority
Tier 1 — Fix now (broken conventions)
- C1+C21 — Rename
audit_optional_in_3_files.py→audit_optional_in_baseline_files.pyand decide whether to extend coverage to allsrc/*.pyor document the 4-file scope honestly. - C2 — Decide whether the ban is enforceable globally; if yes, build the extension; if no, update
docs/AGENTS.mdto honestly say "enforced on 4 baseline files; see cruft_elimination_20260627 for the rest". - C3+C18 — Either build
scripts/audit_imports.pyand the boundary-layer audit, or explicitly mark them as to-be-implemented. - C5 — Replace
Result[str, ErrorInfo]→Result[str]everywhere inguide_ai_client.md. - C16+C17 — Rewrite the contradictory sections of
python.md§10 andtype_aliases.mdline 19 to reflect post-2026-06-25 reality.
Tier 2 — Fix in next docs sync track
- C6 — Update
RAGChunkschema in guides. - C7+C8+C9 — Update counts in
docs/Readme.md. - C11+C12+C13+C14+C15 — Reconcile
tracks.mdandchronology.mdagainst actual shipped state. - C10 — Clarify dataclass location split in
metadata_promotion_20260624spec.
Tier 3 — Followup track (not blocking)
- C4 — Decide whether main-repo pre-commit enforcement is needed.
- C19 — Add OBLITERATE principle to
error_handling.md. - C20 — Reconcile baseline numbers.