fix(ci): address PR review — filter comments, trim whitespace, warn on missing scripts
Address all review comments from Hoshe and Tyre on PR #27: - Remove 2>/dev/null from pre-pr-fixtures (critical: swallowed errors) - Remove dead _file_type function - Check 4: error on districts with no locations declared - Check 5: print advisory message when skipping - Check 8: cross-file line ID uniqueness (not just per-file) - Check 9: document D-034 asymmetric relationships in docstring - Document regex fallback rationale in _scan_knowledge - Add D-035 decision trace to schema descriptions - Use concrete protocol version in DEVOPS.md example - Amend D-035 with focused (9th mood) and greeting (14th situation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -149,7 +149,7 @@ pre-pr-validate: validate-content check-fact-ids
|
||||
|
||||
pre-pr-fixtures:
|
||||
@echo "Checking fixture staleness..."
|
||||
@cd server && cargo test --test gen_fixtures -- --ignored 2>/dev/null
|
||||
@cd server && cargo test --test gen_fixtures -- --ignored
|
||||
@if git diff --quiet client/tests/fixtures/; then \
|
||||
echo "--- Fixtures: UP TO DATE ---"; \
|
||||
else \
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"description": "Situation contexts when this line can fire (D-035: list<enum>)"
|
||||
"description": "Situation contexts when this line can fire (D-035: list<enum>). greeting added Sprint 8 for PC dialogue pools"
|
||||
},
|
||||
"topic": {
|
||||
"type": "array",
|
||||
@@ -96,7 +96,7 @@
|
||||
]
|
||||
},
|
||||
"uniqueItems": true,
|
||||
"description": "Mood tags for selection weighting (D-035: list<enum>)"
|
||||
"description": "Mood tags for selection weighting (D-035: list<enum>, v0.1 8 moods + focused added Sprint 8)"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
|
||||
@@ -82,6 +82,7 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Cross-reference:** Dialogue architecture ([D-028](#d-028-dialogue-architecture--tagged-line-pools-with-four-relational-layers))
|
||||
- **Raised by:** Gestalt (schema design, Round 1/2), Mellanie (authoring validation, Round 1/2). Converged across both agents in Round 2.
|
||||
- **Dissent:** None. Minor consolidations: Mellanie's 8 moods mapped to Gestalt's 8 (different names, same concepts). Mellanie's `crime` topic deliberately excluded (NPCs think of it as `cargo` or `money`).
|
||||
- **Amendment (Sprint 8):** `focused` added as 9th mood (used in Kael dialogue at The Terminal and maintenance corridors). `greeting` added as 14th situation (used in PC dialogue pools for initial contact lines). Schema updated to match.
|
||||
|
||||
### D-036: Sova Transit District / Krenn System as v0.1 setting
|
||||
- **Date:** 2026-02-11
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ If `pre-pr-fixtures` fails, your protocol changes require fixture regeneration:
|
||||
```bash
|
||||
make fixtures
|
||||
git add client/tests/fixtures/
|
||||
git commit -m "chore(fixtures): regenerate for protocol vN"
|
||||
git commit -m "chore(fixtures): regenerate for protocol v8"
|
||||
```
|
||||
|
||||
The fixture staleness check is a **blocker** (exit 1) — stale fixtures cause false positive client tests.
|
||||
|
||||
+30
-20
@@ -82,17 +82,6 @@ def _load_yaml(path: Path) -> dict | list | None:
|
||||
return None
|
||||
|
||||
|
||||
def _file_type(path: Path) -> str | None:
|
||||
"""Determine content type from path (npc, dialogue, monologue, district, triangle)."""
|
||||
rel = path.relative_to(CONTENT_DIR)
|
||||
for part in reversed(rel.parts[:-1]):
|
||||
if part in ("npcs", "dialogue", "monologue", "triangles", "locations"):
|
||||
return part
|
||||
if path.name == "district.yaml":
|
||||
return "district"
|
||||
return None
|
||||
|
||||
|
||||
def _district_dir(path: Path) -> Path | None:
|
||||
"""Find the district directory containing this file."""
|
||||
parts = path.resolve().parts
|
||||
@@ -162,7 +151,8 @@ class ContentIndex:
|
||||
continue
|
||||
data = _load_yaml(path)
|
||||
if not data or not isinstance(data, dict):
|
||||
# Try line-by-line grep for fact_id definitions
|
||||
# Stub catalogs (comment-only or flat key-value) don't parse
|
||||
# as dicts. Fall back to regex extraction of fact_id lines.
|
||||
try:
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
@@ -289,7 +279,12 @@ class ContentIndex:
|
||||
continue
|
||||
district_key = str(district_dir)
|
||||
district_locs = self.locations.get(district_key, set())
|
||||
if district_locs and location not in district_locs:
|
||||
if not district_locs:
|
||||
print(f'XREF ERROR: dialogue location "{location}" references district with no locations declared')
|
||||
print(f" In: {_rel(path)}")
|
||||
print(f" District: {_rel(district_dir / 'district.yaml')}")
|
||||
errors += 1
|
||||
elif location not in district_locs:
|
||||
print(f'XREF ERROR: dialogue location "{location}" not in district')
|
||||
print(f" In: {_rel(path)}")
|
||||
print(f" District locations: {', '.join(sorted(district_locs))}")
|
||||
@@ -299,7 +294,7 @@ class ContentIndex:
|
||||
def _check_5_fact_ids(self) -> int:
|
||||
"""Check that all referenced fact_ids resolve to knowledge catalogs."""
|
||||
if not self.fact_ids:
|
||||
# Advisory mode: catalogs not populated yet
|
||||
print(" Check 5 (fact_ids): SKIPPED — no canonical fact_ids in knowledge catalogs yet")
|
||||
return 0
|
||||
|
||||
errors = 0
|
||||
@@ -414,31 +409,46 @@ class ContentIndex:
|
||||
return warnings
|
||||
|
||||
def _check_8_dialogue_line_ids(self) -> int:
|
||||
"""Check that dialogue line IDs are unique within each file."""
|
||||
"""Check that dialogue line IDs are unique across all dialogue files."""
|
||||
errors = 0
|
||||
# Cross-file uniqueness: same line ID in two files is an error
|
||||
global_seen: dict[str, Path] = {}
|
||||
for path, data in self.dialogue_files:
|
||||
lines = data.get("lines", [])
|
||||
if not isinstance(lines, list):
|
||||
continue
|
||||
seen: dict[str, int] = {}
|
||||
local_seen: dict[str, int] = {}
|
||||
for i, line in enumerate(lines, 1):
|
||||
if not isinstance(line, dict):
|
||||
continue
|
||||
line_id = line.get("id")
|
||||
if not line_id:
|
||||
continue
|
||||
if line_id in seen:
|
||||
# Per-file duplicate
|
||||
if line_id in local_seen:
|
||||
print(f'XREF ERROR: duplicate dialogue line id "{line_id}"')
|
||||
print(f" In: {_rel(path)}")
|
||||
print(f" First: line {seen[line_id]}")
|
||||
print(f" First: line {local_seen[line_id]}")
|
||||
print(f" Duplicate: line {i}")
|
||||
errors += 1
|
||||
else:
|
||||
seen[line_id] = i
|
||||
local_seen[line_id] = i
|
||||
# Cross-file duplicate
|
||||
if line_id in global_seen and global_seen[line_id] != path:
|
||||
print(f'XREF ERROR: dialogue line id "{line_id}" used in multiple files')
|
||||
print(f" First: {_rel(global_seen[line_id])}")
|
||||
print(f" Also in: {_rel(path)}")
|
||||
errors += 1
|
||||
elif line_id not in global_seen:
|
||||
global_seen[line_id] = path
|
||||
return errors
|
||||
|
||||
def _check_9_bidirectional_relationships(self) -> int:
|
||||
"""Check that NPC relationships have reciprocal entries."""
|
||||
"""Check that NPC relationships have reciprocal entries.
|
||||
|
||||
WARNING severity: asymmetric relationships are valid by design (D-034
|
||||
allows one-directional awareness). This check flags them for review,
|
||||
not as errors."""
|
||||
warnings = 0
|
||||
# Build map: canonical_id -> set of relationship targets
|
||||
npc_rels: dict[str, set[str]] = {}
|
||||
|
||||
Reference in New Issue
Block a user