Files
Magnus HedemarkandGitHub b6a96eb22e feat: add ascii-city-engine skill (#321)
* feat: add ascii-city-engine skill

New skill teaching portable first-person colored-ASCII city engines and
small GIS-derived city packs:

- references/engine-architecture.md: terrain height function, ground-
  attached pedestrian physics (feet_z=terrain(x,y), step/slope limits,
  building-footprint collision with wall sliding), fisheye-corrected
  raycast-to-ASCII pipeline, glyph density ladder, deterministic color.
- references/city-provider-contract.md: manifest + world-tile semantics,
  single-height-per-column v1 limit, reserved surface-graph extension.
- references/gis-ingestion.md: USGS 3DEP/OSM/municipal source classes,
  meter-CRS reprojection, unit checks, 3.2 m/floor height fallback,
  provenance/confidence recording.
- references/raleigh-poc.md: numeric downtown bbox, named sources with
  URLs, acquire/convert/validate commands, 4 human acceptance checks.
- templates/: city-pack-manifest + world JSON Schemas.
- scripts/validate-city-pack.py: pure-stdlib offline validator (78 rules).
- assets/raleigh-downtown-sample/: committed coarse pack (30 real OSM
  buildings, 25 road/path surfaces, 78x64 10 m terrain) <= 64K.
- assets/ascii-city-engine.html: dependency-free Canvas 2D engine.
- evals/evals.json: 6 schema-valid cases incl. runtime-specificity and
  large-GIS-commit refusal boundaries.
- README.md catalog entry.

AI-assisted contribution (Hermes Agent, spec-driven-development pipeline).

* chore: regenerate claude marketplace for ascii-city-engine

* chore: regenerate codex plugin and llms.txt for ascii-city-engine

* fix(ascii-city-engine): address droid-review findings on PR #321

1. [P2] Engine scaffold now reads manifest.json for spawn + first tile
   instead of hardcoding (315,385) — restores pack interchangeability.
2. [P2] Validator all_points() guards isinstance(dict) so structurally
   invalid tiles report FAIL instead of crashing with AttributeError.
3. [P2] raleigh-poc acceptance walk: correct duration/elevation math
   (~25s/~100m to ~100.9m at (315,505); ~95s to north edge ~103.0m).
4. [P3] known-limitations: match committed data — 5/30 explicit OSM
   height (0.88), 25 floor-estimated (0.72), not ~26%/0.55/0.35.
5. [P3] bbox: document tangent-plane 632x779m vs normalized 630x770m
   working extent instead of asserting 630x770 as the conversion result.
6. [P3][security] cap polygon vertices (2000), elevation cells (4M),
   feature count to prevent O(n^2) CPU-exhaustion on crafted packs.

Verified: sample pack 79/79 PASS exit 0; broken fixture exit 1; droid
AttributeError repro now FAILs cleanly; 5000-vertex footprint rejected
in 58ms; node --check OK; validate-skills.rb 154 green; blocklist clean.
2026-08-15 21:13:11 -04:00

47 lines
1.7 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "city-pack-manifest.schema.json",
"title": "ASCII city pack manifest",
"type": "object",
"required": ["name", "version", "crs", "bounds", "tiles", "provenance"],
"properties": {
"name": {"type": "string", "minLength": 1},
"version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
"crs": {"type": "string", "minLength": 1},
"bounds": {
"type": "object",
"required": ["min_x", "min_y", "max_x", "max_y"],
"properties": {
"min_x": {"type": "number"}, "min_y": {"type": "number"},
"max_x": {"type": "number"}, "max_y": {"type": "number"}
},
"additionalProperties": false
},
"tiles": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
"spawn": {
"type": "object",
"required": ["x", "y", "heading_deg"],
"properties": {"x": {"type": "number"}, "y": {"type": "number"}, "heading_deg": {"type": "number"}},
"additionalProperties": false
},
"provenance": {
"type": "array", "minItems": 1,
"items": {
"type": "object",
"required": ["name", "url", "license", "retrieved"],
"properties": {
"name": {"type": "string", "minLength": 1},
"url": {"type": "string", "pattern": "^https://"},
"license": {"type": "string", "minLength": 1},
"retrieved": {"type": "string", "format": "date"},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"notes": {"type": "string"}
},
"additionalProperties": true
}
},
"extensions": {"type": "object"}
},
"additionalProperties": false
}