#887: decisions-orphan-tickets CLI — new tooling/db/decision orphan-tickets subcommand that scans tickets with a decision_ref not matching any decision in the DB. Surfaces silently orphaned tickets from typo'd or renumbered D-IDs. Makefile target: make decisions-orphan-tickets.
#888: meta.schema_version → monotonic semver — replaces SHA-1 hash in meta.schema_version with an orderable semver string ("1.0.0"). Old SHA preserved in new schema_sha column for tamper detection. check-systems-db-stamp now validates semver format and rejects legacy SHA-hex values. Enables future savegame migration lineage (Phase 5+).
New `tooling/db/decision orphan-tickets` subcommand scans tickets with
a decision_ref that doesn't match any row in the decisions table.
Surfaces silently orphaned tickets from typo'd or renumbered D-IDs.
Makefile target: `make decisions-orphan-tickets`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace SHA-1 hash in meta.schema_version with an orderable semver
string ("1.0.0"). SHA preserved in new schema_sha column for tamper
detection. Enables savegame migration lineage in Phase 5+ — saves can
record their schema version and determine which migrations to apply.
Updated both generators, check-systems-db-stamp validation (rejects
old SHA-hex values), schema DDL, and asset-pipeline docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary: Core logic is solid — SQL queries are correct, semver validation is well-constructed, and the two-pass sync approach is sound. Three issues need addressing.
#
File
Issue
1
tooling/db/decisions_sync.py
refs_created counter increments unconditionally after INSERT OR IGNORE (always reports N refs even if 0 were new). Dead except IntegrityError block — INSERT OR IGNORE never raises. Fix: check cursor.rowcount > 0 before incrementing, remove dead except.
SCHEMA_VERSION is two independent constants with no enforcement they stay in sync. First bump following the docs (which only mention import_economics.py) will silently diverge.
3
tooling/db/decision (wrapper)
Usage comment omits show and orphan-tickets subcommands — undiscoverable from the wrapper.
Tyre (Architecture): REQUEST_CHANGES
Summary: Both tickets are sound in intent. The semver migration is well-conceived — the "old SHA-1 forces regen-db" path via bad_version is correct behavior. Three architectural issues to address.
#
File
Issue
1
import_economics.py + generate_atlas.py
SCHEMA_VERSION is duplicated as two independent literals. Docs say to bump only import_economics.py. Extract to a shared module, or add a cross-row equality check to the stamp tool.
2
tooling/planet-gen/generate_atlas.py
Bare except Exception: pass in ensure_atlas_schema swallows all errors (locked DB, permissions, malformed SQL). Should match import_economics.py's _add_column pattern: catch sqlite3.OperationalError, re-raise if not "duplicate column".
3
tooling/check-systems-db-stamp
No cross-generator schema_version agreement check. Two generators could write different versions and it passes silently. Add a post-validation equality assertion.
Verdict: CHANGES REQUESTED
Deduplicated issues (5 unique):
refs_created counter inflation + dead except block in decisions_sync.py
SCHEMA_VERSION divergence risk — extract to shared module OR add cross-row check to stamp tool
Bare except Exception: pass in generate_atlas.py — narrow to OperationalError + "duplicate column" check
check-systems-db-stamp needs cross-generator version agreement assertion
tooling/db/decision wrapper usage block missing show and orphan-tickets
## Review: sprint-38/ci → main (type: code)
### Hoshe (Code Quality): REQUEST_CHANGES
**Summary:** Core logic is solid — SQL queries are correct, semver validation is well-constructed, and the two-pass sync approach is sound. Three issues need addressing.
| # | File | Issue |
|---|------|-------|
| 1 | `tooling/db/decisions_sync.py` | `refs_created` counter increments unconditionally after `INSERT OR IGNORE` (always reports N refs even if 0 were new). Dead `except IntegrityError` block — INSERT OR IGNORE never raises. Fix: check `cursor.rowcount > 0` before incrementing, remove dead except. |
| 2 | `tooling/db/decisions_sync.py` + `tooling/planet-gen/generate_atlas.py` | `SCHEMA_VERSION` is two independent constants with no enforcement they stay in sync. First bump following the docs (which only mention `import_economics.py`) will silently diverge. |
| 3 | `tooling/db/decision` (wrapper) | Usage comment omits `show` and `orphan-tickets` subcommands — undiscoverable from the wrapper. |
### Tyre (Architecture): REQUEST_CHANGES
**Summary:** Both tickets are sound in intent. The semver migration is well-conceived — the "old SHA-1 forces regen-db" path via `bad_version` is correct behavior. Three architectural issues to address.
| # | File | Issue |
|---|------|-------|
| 1 | `import_economics.py` + `generate_atlas.py` | `SCHEMA_VERSION` is duplicated as two independent literals. Docs say to bump only `import_economics.py`. Extract to a shared module, or add a cross-row equality check to the stamp tool. |
| 2 | `tooling/planet-gen/generate_atlas.py` | Bare `except Exception: pass` in `ensure_atlas_schema` swallows all errors (locked DB, permissions, malformed SQL). Should match `import_economics.py`'s `_add_column` pattern: catch `sqlite3.OperationalError`, re-raise if not "duplicate column". |
| 3 | `tooling/check-systems-db-stamp` | No cross-generator `schema_version` agreement check. Two generators could write different versions and it passes silently. Add a post-validation equality assertion. |
### Verdict: CHANGES REQUESTED
**Deduplicated issues (5 unique):**
1. `refs_created` counter inflation + dead except block in `decisions_sync.py`
2. `SCHEMA_VERSION` divergence risk — extract to shared module OR add cross-row check to stamp tool
3. Bare `except Exception: pass` in `generate_atlas.py` — narrow to `OperationalError` + "duplicate column" check
4. `check-systems-db-stamp` needs cross-generator version agreement assertion
5. `tooling/db/decision` wrapper usage block missing `show` and `orphan-tickets`
1. decisions_sync.py: fix refs_created inflation (check rowcount),
remove dead IntegrityError except block
2. Extract SCHEMA_VERSION to shared tooling/schema_version.py —
both generators import from single source of truth
3. generate_atlas.py: narrow bare except to OperationalError +
"duplicate column" check
4. check-systems-db-stamp: add cross-generator schema_version
agreement assertion (defense-in-depth)
5. decision wrapper: add show + orphan-tickets to usage text
6. Add schema_version.py to all three source watch lists
(GENERATOR_SOURCES, IMPORT_ECONOMICS_SOURCES, generate_atlas
_write_stamp) — prevents silent staleness on version bump
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FIXED (narrowed to OperationalError + "duplicate column")
4
No cross-generator agreement check
FIXED (cross-row equality assertion added)
5
Wrapper usage missing subcommands
FIXED
New issue (both reviewers)
.claude/rules/asset-pipeline.md and server/data/systems-schema.sql still reference tooling/economy-db/import_economics.py as the home of SCHEMA_VERSION. The constant now lives in tooling/schema_version.py. Three-line fix:
asset-pipeline.md line ~55: update path
asset-pipeline.md line ~186: update path
systems-schema.sql meta table comment: update path
asset-pipeline.md (2 locations) and systems-schema.sql still pointed
at import_economics.py as the SCHEMA_VERSION home after the extraction
to a shared module.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Sprint 38 CI — two tooling improvements:
tooling/db/decision orphan-ticketssubcommand that scans tickets with adecision_refnot matching any decision in the DB. Surfaces silently orphaned tickets from typo'd or renumbered D-IDs. Makefile target:make decisions-orphan-tickets.meta.schema_versionwith an orderable semver string ("1.0.0"). Old SHA preserved in newschema_shacolumn for tamper detection.check-systems-db-stampnow validates semver format and rejects legacy SHA-hex values. Enables future savegame migration lineage (Phase 5+).Changed files
tooling/db/decisions_sync.pyorphan-ticketssubcommandMakefiledecisions-orphan-ticketstargettooling/economy-db/import_economics.pySCHEMA_VERSIONconstant, semver stamp,schema_shamigrationtooling/planet-gen/generate_atlas.pySCHEMA_VERSIONconstant, semver stamp, migrationtooling/check-systems-db-stampschema_shaawarenessserver/data/systems-schema.sqlmetatable DDL updated withschema_shacolumnserver/data/systems.db.claude/rules/asset-pipeline.mdCHANGELOG.mdVerification
make decisions-orphan-tickets— runs cleanly, found 14 orphan tickets (all pre-existing stale refs)make regen-db— succeedsmake check-systems-db— reportsOK — 2 generator(s) up to dateruff check tooling/— all checks passedReplace SHA-1 hash in meta.schema_version with an orderable semver string ("1.0.0"). SHA preserved in new schema_sha column for tamper detection. Enables savegame migration lineage in Phase 5+ — saves can record their schema version and determine which migrations to apply. Updated both generators, check-systems-db-stamp validation (rejects old SHA-hex values), schema DDL, and asset-pipeline docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>Review: sprint-38/ci → main (type: code)
Hoshe (Code Quality): REQUEST_CHANGES
Summary: Core logic is solid — SQL queries are correct, semver validation is well-constructed, and the two-pass sync approach is sound. Three issues need addressing.
tooling/db/decisions_sync.pyrefs_createdcounter increments unconditionally afterINSERT OR IGNORE(always reports N refs even if 0 were new). Deadexcept IntegrityErrorblock — INSERT OR IGNORE never raises. Fix: checkcursor.rowcount > 0before incrementing, remove dead except.tooling/db/decisions_sync.py+tooling/planet-gen/generate_atlas.pySCHEMA_VERSIONis two independent constants with no enforcement they stay in sync. First bump following the docs (which only mentionimport_economics.py) will silently diverge.tooling/db/decision(wrapper)showandorphan-ticketssubcommands — undiscoverable from the wrapper.Tyre (Architecture): REQUEST_CHANGES
Summary: Both tickets are sound in intent. The semver migration is well-conceived — the "old SHA-1 forces regen-db" path via
bad_versionis correct behavior. Three architectural issues to address.import_economics.py+generate_atlas.pySCHEMA_VERSIONis duplicated as two independent literals. Docs say to bump onlyimport_economics.py. Extract to a shared module, or add a cross-row equality check to the stamp tool.tooling/planet-gen/generate_atlas.pyexcept Exception: passinensure_atlas_schemaswallows all errors (locked DB, permissions, malformed SQL). Should matchimport_economics.py's_add_columnpattern: catchsqlite3.OperationalError, re-raise if not "duplicate column".tooling/check-systems-db-stampschema_versionagreement check. Two generators could write different versions and it passes silently. Add a post-validation equality assertion.Verdict: CHANGES REQUESTED
Deduplicated issues (5 unique):
refs_createdcounter inflation + dead except block indecisions_sync.pySCHEMA_VERSIONdivergence risk — extract to shared module OR add cross-row check to stamp toolexcept Exception: passingenerate_atlas.py— narrow toOperationalError+ "duplicate column" checkcheck-systems-db-stampneeds cross-generator version agreement assertiontooling/db/decisionwrapper usage block missingshowandorphan-ticketsReview Round 2: sprint-38/ci → main
Previous issues: ALL FIXED
refs_createdcounter + dead exceptSCHEMA_VERSIONduplicatedtooling/schema_version.py)except Exception: passin generate_atlasNew issue (both reviewers)
.claude/rules/asset-pipeline.mdandserver/data/systems-schema.sqlstill referencetooling/economy-db/import_economics.pyas the home ofSCHEMA_VERSION. The constant now lives intooling/schema_version.py. Three-line fix:asset-pipeline.mdline ~55: update pathasset-pipeline.mdline ~186: update pathsystems-schema.sqlmeta table comment: update pathVerdict: CHANGES REQUESTED (minor — doc path references only)
Pull request closed