Repository navigation
feat(stac): prefix-aware STAC typing + write-path safety (#256) - #663
Merged
Merged
Conversation
Specifies prefix-aware TypeScript typing for StacExtensionProperties so the writer's debrief:* property access sites gain named-slot typing flowing from LinkML. Closes the #240-deferred Article II.1 audit finding. Marks backlog item 256 as specified and links to specs/256-prefix-aware-stac-typing/spec.md. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
Contributor
📋 Spec NavigatorReview this PR's specs interactively:
|
Contributor
📋 Backlog NavigatorReview this PR's BACKLOG.md edits interactively: Updated for 89a4551 — the navigator reads BACKLOG.md live from this PR's head branch, so no rebuild is needed. |
Adds plan.md, research.md, data-model.md, contracts/, quickstart.md, and the cached blog opener for the prefix-aware StacExtensionProperties typing feature. Implementation route confirmed: extend the gen-typescript post-processor (generate.py) to emit debrief:-prefixed slot keys derived from LinkML slot_uri, so the STAC writers' props['debrief:*'] access sites gain typed slots with no call-site rewrites. Typing-only; reuses the existing src/generated drift gate. Constitution check passes (strengthens Article II.1). Marks backlog item 256 as planned. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
Re-plan after /speckit.review folded follow-ups into this PR: - Prefix-aware transform now covers StacExtensionProperties, StacSummaries, and StacAsset (schema-driven via slot_uri — handles the divergent name->slot_uri conventions a per-class text rule cannot). - Model debrief:toolId + debrief:snapshotTimestamp on StacAsset (additive LinkML slots; removes the stacService.ts:674 hand-cast). Drop debrief:label (a GeoJSON feature/MCP annotation key, not a STAC property). - Re-type both hosts' write-path props from Record<string,unknown> to StacItemProperties so write-side typos fail the build (ADR-033 / Art IV.5). - FR-002 covered by a pure-function transform unit test (synthetic slot). Updates spec/plan/research/data-model/contracts/quickstart/opening-context and refreshes agent context. No code changed yet. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
… typing) 31 tasks across 6 phases: - Phase 2 (foundational): StacAsset slots + schema-driven prefix transform (pure fn + slot_uri loader + wiring) + regen — blocks all stories. - US1 (P1): FR-002 proven by pure-function unit test + convention guard. - US2 (P2): remove read casts, re-type both hosts' write-path props to StacItemProperties, remove StacAsset hand-cast; type-level read+write+asset assertions. - US3 (P3): byte-invariance golden + StacAsset round-trip + #240 audit closure. - Polish: evidence (round-trip proof), shipped post, /speckit.pr (T031 last). https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
…ots (#256) Phase 2 (foundational): - Add prefix_extension_slots() pure fn + _load_extension_slot_uri_map() loader to generate.py; wire one schema-driven step into generate_typescript() that rewrites modelled slot keys to their slot_uri across StacExtensionProperties, StacSummaries, and StacAsset (RuntimeError self-guards per existing convention). - Model debrief:toolId + debrief:snapshotTimestamp as optional StacAsset slots. - Regenerate TS + Pydantic (additive); deterministic (byte-identical re-run); 180 STAC/idempotency schema tests pass. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
Phase 3 (US1): test_stac_prefix_transform.py (7 tests) proves the transform is a pure schema-driven function (new debrief:* slot flows through with no generator edit), the loader resolves 3 classes, and the committed types.ts emits every modelled slot under its colon key (convention guard) + structural counts. Worked FR-002 demo recorded in evidence/usage-example.md. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
Phase 4 (US2): - vscode stacService: drop read-site casts; re-type write-path props to StacItemProperties (remove the Record<string,unknown> widening + ADR-011 eslint-disable); remove the StacAsset hand-cast for debrief:toolId. - web-shell stacWriterIdb: re-type props to StacItemProperties; drop the props-as-StacItem['properties'] cast. - Typing the debrief:provenance_log slot surfaced the #240 generated-vs-hybrid PropertiesProvenanceEntry divergence (previously masked by the as-cast); resolved with an explicit typed persistence->domain narrowing bridge on the read (write is narrow<:wide, no cast). - Add tests/ts/stac-prefix-typing-256.test.ts (6 type-level read/write/asset assertions). Both apps typecheck clean; 111 vscode + 54 web-shell unit tests pass. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
…status-JCx2R # Conflicts: # CLAUDE.md
Phase 5 (US3): - Byte-stable round-trip test for the new StacAsset debrief:toolId / debrief:snapshotTimestamp keys (FR-008/SC-004). - ADR-040 records the schema-driven mechanism, write-path re-typing, label exclusion, the provenance_log hybrid-divergence discovery, and the #240 Article II.1 deferral closure; issues.md logged. - Fix ruff ANN202 on the prefix-transform test helper (clears CI Ruff Lint). https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
Contributor
🚀 Preview DeploymentsCode Server (Full VS Code Extension)Browser-based VS Code with the Debrief extension and sample data pre-installed. Web Shell (Standalone App)Use this for Playwright testing and demos - runs outside Storybook. Storybook (Component Library)Browse all components, stories, and documentation. Briefing Renderer (Air-gapped Storyboard Player — spec #264)Standalone SPA that plays a Storyboard from a All Links
|
Contributor
Schema Docs PreviewInteractive site (clickable Mermaid, search, navigation)Markdown sources (textual review on GitHub)All Links
|
…ed post (#256) Phase 6: full gate verified locally (ruff/eslint/pyright/tsc clean, 2204 pytest pass, schemas+vscode+web-shell vitest pass). Evidence: test-summary.md (YAML front matter), round-trip-evidence.md, generated-diff.md, usage-example.md; media/shipped-post.md (Building Prefix-Aware STAC Typing). https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
CI 'Ruff Lint' runs 'ruff format --check'; my two new Python files needed formatting (ruff check already passed). Cosmetic only — 36 tests pass, generated artefacts drift-clean. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
The liveBacklog round-trip test (apps/backlog-navigator) parses the live BACKLOG.md and asserts byte-identical parse->serialize. My manual ~~-wrap + leftover 'implementing' status was non-canonical (the serializer strikes a row iff status==='complete'). Set status=complete and re-serialised via the navigator's own serializer. Fixes the 'Test & Lint' failure. https://claude.ai/code/session_01JZ63MUkLs28LsSStBDbrh2
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the spec-240–deferred promise true: modelled
debrief:*STAC fields now flow automatically to the writers' typed surface (read and write). LinkML'sgen-typescriptstrips thedebrief:prefix, so a naiveStacItem.properties: StacExtensionPropertiesintersection matched none of the writers' real, prefixed access sites — they went through an untypedRecordbag withascasts.shared/schemas/scripts/generate.py, pure fnprefix_extension_slots) rewrites modelled slot keys to their LinkMLslot_uriverbatim across three classes:StacExtensionProperties(5),StacSummaries(3),StacAsset(2 new).propsre-typed fromRecord<string, unknown>toStacItemProperties, so write-side typos fail the build (the half a naive fix misses; ADR-033 / Article IV.5).debrief:toolId+debrief:snapshotTimestampas additiveStacAssetslots; removes the hand-castasset as StacAsset & { 'debrief:toolId'?: string }.src/generateddrift gate.Changes
Schema + generator (foundational)
stac.yaml: two additive optionalStacAssetslots (tool_id→debrief:toolId,snapshot_timestamp→debrief:snapshotTimestamp).generate.py:prefix_extension_slots()+_load_extension_slot_uri_map(); one wired step over the three classes withRuntimeErrorself-guards. Regenerated TS + Pydantic (additive); deterministic.Writer hosts
apps/vscode/src/services/stacService.ts: drop read-site casts; re-type write-pathprops(removes theRecordwidening + ADR-011 eslint-disable); remove theStacAssetdebrief:toolIdhand-cast.apps/web-shell/src/services/stacWriterIdb.ts: re-type write-pathprops; drop theprops as StacItem['properties']cast.debrief:provenance_logsurfaced a latent Implement tool parameter context menus (Feature 091) #240 divergence (generated wide vs component narrowed-hybridPropertiesProvenanceEntry) that theas-cast had masked — resolved with an explicit typed persistence→domain narrowing bridge on the read.Decision:
debrief:labelexcludedInvestigation showed it's a GeoJSON feature property / MCP annotation, not a STAC item/asset key; modelling it would re-create the mismatch this feature removes. Left for a future
BaseFeaturePropertieschange.Evidence
Test Results
Verified: FR-002 (new slot flows with no generator edit — pure-fn unit test); FR-004/FR-012 (read and write typos fail the build); FR-008/SC-004 (byte-stable round-trip incl. new asset keys); SC-007 (asset hand-cast removed); deterministic regen + drift-clean.
Generated diff (before → after)
export interface StacExtensionProperties { - platforms?: PlatformRecord[], + 'debrief:platforms'?: PlatformRecord[], - ... + 'debrief:...' } export interface StacSummaries { - debrief_platforms?: PlatformRecord[], + 'debrief:platforms'?: PlatformRecord[], } export interface StacAsset { href: string, // untouched (no extension slot_uri) + 'debrief:toolId'?: string, + 'debrief:snapshotTimestamp'?: string, }Full artefacts:
specs/256-prefix-aware-stac-typing/evidence/(test-summary.md,round-trip-evidence.md,generated-diff.md,usage-example.md).Test Plan
uv run pytest(incl. newtest_stac_prefix_transform.py,test_stac_roundtrip.py)pnpm --filter @debrief/schemas typecheck && test+tsc --noEmiton both appsruff check+ruff format --check+pyright+eslintcleansrc/generateddrift-cleanRelated
specs/256-prefix-aware-stac-typing/spec.mdspecs/256-prefix-aware-stac-typing/tasks.mddocs/project_notes/decisions.md); closes the Implement tool parameter context menus (Feature 091) #240 Article II.1 deferral📖 Review in Spec Navigator: https://debrief.github.io/debrief-future/spec-navigator/?pr=663
Generated by Claude Code