Skip to content

Implement v18 property projection reads#99

Merged
flyingrobots merged 10 commits into
mainfrom
v18-continuum-slices-26-30
May 23, 2026
Merged

Implement v18 property projection reads#99
flyingrobots merged 10 commits into
mainfrom
v18-continuum-slices-26-30

Conversation

@flyingrobots
Copy link
Copy Markdown
Member

@flyingrobots flyingrobots commented May 23, 2026

Summary

  • close the post-25 property projection runway and mark slice 26 complete
  • add runtime-backed legacy property key, value, visible record, and projection collection nouns
  • add node and edge property projections over visible WarpState facts
  • route query node properties, edge properties, and edge-list props through projection-backed compatibility records

Verification

  • npx vitest run test/unit/domain/graph/LegacyPropertyProjection.test.ts --reporter=verbose
  • npx vitest run test/unit/domain/services/NodePropertyProjection.test.ts --reporter=verbose
  • npx vitest run test/unit/domain/services/EdgePropertyProjection.test.ts --reporter=verbose
  • npx vitest run test/unit/domain/services/QueryReadsPropertyProjection.test.ts test/unit/domain/services/NodePropertyProjection.test.ts test/unit/domain/services/EdgePropertyProjection.test.ts --reporter=verbose
  • npx vitest run test/unit/domain/WarpGraph.query.test.ts test/unit/domain/WarpGraph.edgeProps.test.ts test/unit/domain/WarpGraph.edgePropVisibility.test.ts test/unit/domain/services/QueryReadsPropertyProjection.test.ts --reporter=verbose
  • npm run typecheck
  • npm run lint
  • npm run lint:sludge
  • npm run lint:quarantine-graduate
  • npx markdownlint-cli2 CHANGELOG.md docs/BEARING.md docs/design/0174-v18-post-25-property-projection-runway/v18-post-25-property-projection-runway.md docs/design/0175-v18-legacy-property-projection-nouns/v18-legacy-property-projection-nouns.md docs/design/0176-v18-node-property-projection/v18-node-property-projection.md docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md docs/design/0178-v18-query-property-projection-reads/v18-query-property-projection-reads.md
  • git diff --check origin/main...HEAD
  • npm run test:local: 484 files / 6945 tests passed
  • pre-push IRONCLAD M9 gates passed

ADR checks

  • This PR does not implement ADR 2 without satisfying ADR 3
  • If this PR touches persisted op formats, I linked the ADR 3 readiness issue (N/A: no persisted op format change)
  • If this PR touches wire compatibility, I confirmed canonical-only ops are still rejected on the wire pre-cutover (N/A: no wire compatibility change)
  • If this PR touches schema constants, I confirmed patch and checkpoint namespaces remain distinct (N/A: no schema constant change)

Summary by CodeRabbit

  • New Features

    • Query property reads now use projection-backed compatibility records for node and edge properties.
  • Bug Fixes

    • Preserved tolerant property-query misses; scoped projection materialization to requested owners.
    • Malformed edge/node property entries are skipped; invalid class-instance carriers rejected.
    • Shared legacy content property key handling made consistent.
  • Documentation

    • Updated v18 implementation cycle, slice tracking, and design docs detailing the property-projection approach.
  • Tests

    • Added comprehensive unit tests verifying projections and query-read routing.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

Implements V18 property projection: runtime-backed legacy key/value nouns and visible record types, deterministic NodePropertyProjection and EdgePropertyProjection services, QueryReads refactor to consume projections, unit tests, and docs/changelog updates.

Changes

V18 Property Projection Implementation

Layer / File(s) Summary
Legacy property key and value nouns
src/domain/graph/LegacyPropertyKeyClassification.ts, src/domain/graph/LegacyNodePropertyKey.ts, src/domain/graph/LegacyEdgePropertyKey.ts, src/domain/graph/LegacyPropertyValue.ts, test/unit/domain/graph/LegacyPropertyProjection.test.ts
Classification constants and runtime-validated frozen wrappers for legacy property keys and values; defensive cloning and validation for PropValue carriers; unit tests for classification, validation, and immutability.
Visible property records & container
src/domain/graph/VisibleNodePropertyRecord.ts, src/domain/graph/VisibleEdgePropertyRecord.ts, src/domain/graph/LegacyPropertyProjection.ts
Immutable visible node/edge property record types and projection container exposing owner-scoped lookup methods with runtime validation.
Shared projection infrastructure & constants
src/domain/graph/LegacyContentPropertyKeys.ts, src/domain/services/KeyCodec.ts, src/domain/services/LegacyPropertyProjectionTarget.ts, src/domain/types/PropValue.ts
Extract legacy content property key constants, re-export them from KeyCodec, add projection-target predicates, and refine PropValue plain-object detection.
NodePropertyProjection service
src/domain/services/NodePropertyProjection.ts
Projects node properties from WarpState by decoding legacy node keys, building deterministic frozen VisibleNodePropertyRecord[] and owner-scoped helpers (fromState, forNode, forNodeRecord).
EdgePropertyProjection service
src/domain/services/EdgePropertyProjection.ts
Projects edge properties from WarpState by decoding legacy edge keys, filtering stale registers by edge birth events, resolving owners, and returning deterministic frozen VisibleEdgePropertyRecord[] (fromState, forEdge, forEdgeRecord).
QueryReads integration & public API
src/domain/services/controllers/QueryReads.ts, src/domain/graph/publicGraphSubstrate.ts, src/domain/utils/StringComparison.ts
Refactors QueryReads to use Node/EdgePropertyProjection and projection-target predicates, builds edge-prop maps from projected records, converts frozen projection outputs to public property objects, exposes projection nouns/types via publicGraphSubstrate, and adds compareStrings util for deterministic ordering.
Projection unit tests and QueryReads tests
test/unit/domain/services/EdgePropertyProjection.test.ts, test/unit/domain/services/NodePropertyProjection.test.ts, test/unit/domain/services/QueryReadsPropertyProjection.test.ts
Vitest suites verifying deterministic projection content, edge-birth freshness filtering, malformed-input tolerance (miss semantics), owner-scoped isolation, and end-to-end QueryReads routing.
Design docs & changelog
CHANGELOG.md, docs/BEARING.md, docs/design/0174-0178/*
Update changelog, BEARING, and design documents to mark slices 26–30 complete, include RED/GREEN narratives, verification outputs, and task tracking for property projection work.

Sequence Diagram(s)

sequenceDiagram
  participant Client as QueryReads
  participant Projection as Node/EdgePropertyProjection
  participant State as WarpState
  participant Record as Visible{Node|Edge}PropertyRecord
  Client->>Projection: request projected props (forNode/forEdge/fromState)
  Projection->>State: iterate state.prop / decode legacy keys
  State-->>Projection: LWW registers
  Projection->>Projection: filter/validate keys, birth-event checks
  Projection->>Record: construct Visible property records
  Projection-->>Client: frozen, sorted Visible property records
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • git-stunts/git-warp#42: Related work on legacy content property key handling (_content and metadata) used by projection materialization and query routing.

Poem

🐰 I nibbled keys where shadows lay,

Frozen records hopped into day.
Birth-event checks keep stale crumbs gone,
QueryReads now follows projection's dawn. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Implement v18 property projection reads' clearly and specifically summarizes the main change: implementing property projection reads for v18, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description includes a clear summary of changes, comprehensive verification steps, and all required ADR checks marked as completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v18-continuum-slices-26-30

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Release Preflight

  • package version: 17.0.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v17.0.1, release workflow will publish.

@flyingrobots
Copy link
Copy Markdown
Member Author

@codex Self-review findings for PR #99.

Severity File Lines Infraction Evidence Recommended mitigation prompt
P1 Major src/domain/services/controllers/QueryReads.ts 148-168 Public query reads can now throw on malformed missing-input carriers instead of returning null / empty results. linearNodeProps() calls state.hasNodeRecord(nodeId) before any tolerant public-query guard. WarpState.hasNodeRecord() delegates through getNodeRecord(), which normalizes to NodeId; NodeId rejects empty strings, NULs, and reserved edge-property prefixes. edgePropsFromState() constructs EdgeRecord.fromLegacyEdge(edge) before checking existence; that constructs NodeId endpoints and EdgeTypeId labels, which reject empty/NUL values. The branch intent is output-preserving projection reads, so this is a behavioral regression risk for public query APIs. Add regression tests proving node/edge property public query reads return null for malformed or invalid nonexistent carriers that previously missed naturally. Then adjust QueryReads or projection APIs so public reads do not instantiate strict runtime nouns until after a tolerant existence/miss decision. Prefer explicit tolerant projection entry points over catching broad errors.
P3 Minor src/domain/services/NodePropertyProjection.ts, src/domain/services/EdgePropertyProjection.ts 36-44, 50-61 Single-owner reads project and allocate all visible property records, then filter. forNode() and forEdge() call fromState() and filter the complete result set. The old query path scanned all legacy properties but only materialized output for matching owners. This change centralizes decoding correctly, but it also makes targeted reads allocate every visible property record in the graph. Add targeted projection paths that scan state.prop once but only construct records for the requested visible owner, and keep fromState() for whole-graph enumeration. Cover with deterministic tests that prove targeted and full projections agree for one owner.
P3 Minor src/domain/graph/LegacyPropertyKeyClassification.ts 3-6 Content compatibility key literals have a duplicated source of truth. _content, _content.mime, and _content.size are declared here while src/domain/services/KeyCodec.ts also exports CONTENT_PROPERTY_KEY, CONTENT_MIME_PROPERTY_KEY, and CONTENT_SIZE_PROPERTY_KEY. This creates drift risk between compatibility classification and the codec. Move the content compatibility key constants to a dependency-neutral domain module, then import that module from both KeyCodec and LegacyPropertyKeyClassification. Add a focused test tying classification behavior to the shared constants.
P5 Nit test/unit/domain/graph/LegacyPropertyProjection.test.ts 59-60 Test fixture uses ambient wall-clock construction as an invalid value carrier. The test intentionally passes new Date() to LegacyPropertyValue under @ts-expect-error. This is not a source-policy violation because it is test code, but it is noisy against the repository's strict no-ambient-time posture. Replace new Date() with a stable invalid carrier, such as an instance of a local InvalidPropertyCarrier test class, so the test remains deterministic and policy-scan-friendly.

@flyingrobots
Copy link
Copy Markdown
Member Author

@codex Resolution summary for the self-review findings.

Finding Severity Outcome Commit
Public property query reads could throw on malformed miss carriers. P1 Major Added malformed node/edge public-query regression coverage and routed linear reads through tolerant projection-target guards before strict runtime record construction. 83223209
Targeted node/edge property reads materialized unrelated owners. P3 Minor Added runtime-backed forNodeRecord() / forEdgeRecord() paths that scan state.prop but only construct records for the requested owner, with corrupt non-target-state isolation coverage. 83223209
Content compatibility key literals were duplicated. P3 Minor Added LegacyContentPropertyKeys as the shared source and re-exported it from KeyCodec; classification now consumes the shared constants. b62e72a7
Test invalid-value fixture used new Date(). P5 Nit Replaced the ambient-time fixture with a deterministic invalid carrier and tightened PropValue validation so class instances are rejected rather than accepted as plain property objects. 83223209
Changelog coverage for review fixes. Docs Recorded the review follow-up under CHANGELOG.md Unreleased / Fixed. dd14007d

Verification run locally before push and again through the pre-push firewall:

Gate Result
Focused Vitest property projection suite PASS, 13 tests
npm run typecheck PASS
npm run lint PASS
npm run lint:sludge PASS
npm run lint:quarantine-graduate PASS
npm run test:local PASS, 484 files / 6943 tests
Pre-push IRONCLAD M9 static gates + unit suite PASS

@github-actions
Copy link
Copy Markdown

Release Preflight

  • package version: 17.0.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v17.0.1, release workflow will publish.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/design/0176-v18-node-property-projection/v18-node-property-projection.md (1)

107-107: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Verification path doesn't match observed output.

Line 107 specifies test/unit/domain/graph/NodePropertyProjection.test.ts, but the observed output at line 117 shows the test is actually located at test/unit/domain/services/NodePropertyProjection.test.ts. The verification command should reflect the actual file location.

📝 Proposed fix
-npx vitest run test/unit/domain/graph/NodePropertyProjection.test.ts --reporter=verbose
+npx vitest run test/unit/domain/services/NodePropertyProjection.test.ts --reporter=verbose
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/design/0176-v18-node-property-projection/v18-node-property-projection.md`
at line 107, Update the verification command to point to the actual test file
location: replace the path test/unit/domain/graph/NodePropertyProjection.test.ts
with test/unit/domain/services/NodePropertyProjection.test.ts in the npx vitest
run invocation so the command runs the NodePropertyProjection.test.ts under the
services directory.
docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md (1)

105-105: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Verification path doesn't match observed output.

Line 105 specifies test/unit/domain/graph/EdgePropertyProjection.test.ts, but the observed output at line 115 shows the test is located at test/unit/domain/services/EdgePropertyProjection.test.ts. The verification command should use the correct path.

📝 Proposed fix
-npx vitest run test/unit/domain/graph/EdgePropertyProjection.test.ts --reporter=verbose
+npx vitest run test/unit/domain/services/EdgePropertyProjection.test.ts --reporter=verbose
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md`
at line 105, The verification command references the wrong test path; update the
npx vitest run command in EdgePropertyProjection documentation to point to the
actual test file location by replacing
test/unit/domain/graph/EdgePropertyProjection.test.ts with
test/unit/domain/services/EdgePropertyProjection.test.ts so the command runs the
observed test (EdgePropertyProjection.test.ts) in the services folder.
🧹 Nitpick comments (2)
src/domain/services/NodePropertyProjection.ts (1)

38-45: ⚖️ Poor tradeoff

Targeted reads allocate all visible property records.

forNode calls fromState() which iterates and constructs all visible node property records, then filters to the requested owner. For graphs with many nodes and properties, this allocates and discards records unnecessarily.

♻️ Potential optimization: targeted iteration path

Add a targeted iteration path that constructs only records matching the requested node ID:

static forNode(state: WarpState, nodeId: string | NodeId): readonly VisibleNodePropertyRecord[] {
  const checkedState = requireWarpState(state);
  const owner = nodeRecordForProjectionTarget(checkedState, nodeId);
  if (owner === null) {
    return Object.freeze([]);
  }
  
  const targetNodeIdStr = owner.id.toString();
  const records: VisibleNodePropertyRecord[] = [];
  
  // Iterate only once and construct only matching records
  for (const [encodedKey, register] of checkedState.prop) {
    if (!encodedKey.startsWith(targetNodeIdStr)) continue;
    const record = nodePropertyRecordForOwnerRegister(owner, encodedKey, register);
    if (record !== null) {
      records.push(record);
    }
  }
  
  records.sort(compareNodePropertyRecords);
  return Object.freeze(records);
}

Keep fromState() for whole-graph enumeration.

Based on learnings: Reviewer self-review flagged that forNode()/forEdge() call fromState() and filter complete result set, causing targeted reads to allocate every visible property record instead of constructing only requested-owner records.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/services/NodePropertyProjection.ts` around lines 38 - 45, forNode
currently builds the entire visible-property projection via fromState() and then
filters, causing wasteful allocations; change it to a targeted iteration: after
resolving checkedState with requireWarpState and the owner via
nodeRecordForProjectionTarget, iterate checkedState.prop and only process
entries whose encoded key begins with owner.id.toString(), call
nodePropertyRecordForOwnerRegister(owner, encodedKey, register) for matches,
collect non-null records, sort with compareNodePropertyRecords, freeze and
return; keep fromState() unchanged for full-graph enumeration.
src/domain/services/EdgePropertyProjection.ts (1)

57-67: ⚖️ Poor tradeoff

Targeted reads allocate all visible property records.

forEdge delegates to fromState() via the code path at line 66, which iterates and constructs all visible edge property records before filtering to the requested owner. For graphs with many edges and properties, this allocates and discards records unnecessarily.

Same performance pattern as NodePropertyProjection.forNode. Consider adding a targeted iteration path that constructs only records matching the requested edge (filter by encoded key prefix early in the loop). Keep fromState() for whole-graph enumeration.

Based on learnings: Reviewer self-review flagged that forNode()/forEdge() call fromState() and filter complete result set, causing targeted reads to allocate every visible property record instead of constructing only requested-owner records.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/services/EdgePropertyProjection.ts` around lines 57 - 67, forEdge
currently delegates to forEdgeRecord/fromState and causes allocation of all
visible property records then filters by owner; implement a targeted iteration
that only constructs records for the requested edge by scanning the state/store
with the encoded owner key prefix and building VisibleEdgePropertyRecord
on-the-fly. Modify EdgePropertyProjection.forEdge to call a new helper (e.g.
forEdgeTargeted or forEdgeFromStatePrefix) that uses requireWarpState(state) and
edgeRecordForProjectionTarget(edge) to compute the prefix, then iterates the
underlying store/entries producing only matching records; keep the existing
fromState() intact for full-graph enumeration and mirror the same change pattern
used for NodePropertyProjection.forNode to maintain symmetry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/domain/services/EdgePropertyProjection.ts`:
- Around line 109-118: The tolerant resolver edgeRecordForProjectionTarget
currently calls EdgeRecord.fromLegacyEdge(edge) which throws on malformed
NodeId/EdgeTypeId; change it to avoid constructing EdgeRecord unless inputs are
valid by wrapping the conversion in a try-catch (or validate fields first) so
malformed/invalid legacy edges cause the function to return null instead of
throwing; specifically update edgeRecordForProjectionTarget to catch errors from
EdgeRecord.fromLegacyEdge(edge) and return null on any exception, then call
state.getEdgeRecord(...) only when conversion succeeds.
- Around line 130-138: The loop in EdgePropertyProjection that calls
EdgeRecord.fromLegacyEdge(keyParts) can throw on malformed identifiers and
should skip invalid entries instead of throwing; update the projection (e.g., in
fromState or the method iterating state.prop) to either validate keyParts.from,
keyParts.to and keyParts.label before calling EdgeRecord.fromLegacyEdge or wrap
the construction and subsequent
state.getEdgeRecord(EdgeRecord.fromLegacyEdge(...).id) in a try-catch that
returns/continues null for that entry; ensure you still call encodeEdgeKey and
visibleEdgeRegister as before but gracefully ignore and continue on malformed
keys so a single bad entry does not abort the full projection.

In `@src/domain/services/NodePropertyProjection.ts`:
- Around line 177-186: Duplicate compareStrings implementation in
NodePropertyProjection.ts and EdgePropertyProjection.ts; extract it to a single
exported utility (e.g., export function compareStrings(left: string, right:
string): number) in a shared module such as src/domain/utils/StringComparison.ts
(or an existing string utilities module), replace the local implementations in
NodePropertyProjection and EdgePropertyProjection with imports from that new
module, and run type checks to ensure both projection files reference the same
exported symbol.
- Around line 83-88: The function nodeRecordForProjectionTarget currently defers
to state.getNodeRecord(nodeId) which can throw for malformed NodeId values;
update nodeRecordForProjectionTarget to catch exceptions from
state.getNodeRecord (or validate nodeId for empty/NUL/reserved prefixes before
calling) and return null on error so it remains tolerant for public query
APIs—refer to nodeRecordForProjectionTarget, state.getNodeRecord, and NodeId
(and note state.hasNodeRecord behavior) when making the change.

---

Outside diff comments:
In
`@docs/design/0176-v18-node-property-projection/v18-node-property-projection.md`:
- Line 107: Update the verification command to point to the actual test file
location: replace the path test/unit/domain/graph/NodePropertyProjection.test.ts
with test/unit/domain/services/NodePropertyProjection.test.ts in the npx vitest
run invocation so the command runs the NodePropertyProjection.test.ts under the
services directory.

In
`@docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md`:
- Line 105: The verification command references the wrong test path; update the
npx vitest run command in EdgePropertyProjection documentation to point to the
actual test file location by replacing
test/unit/domain/graph/EdgePropertyProjection.test.ts with
test/unit/domain/services/EdgePropertyProjection.test.ts so the command runs the
observed test (EdgePropertyProjection.test.ts) in the services folder.

---

Nitpick comments:
In `@src/domain/services/EdgePropertyProjection.ts`:
- Around line 57-67: forEdge currently delegates to forEdgeRecord/fromState and
causes allocation of all visible property records then filters by owner;
implement a targeted iteration that only constructs records for the requested
edge by scanning the state/store with the encoded owner key prefix and building
VisibleEdgePropertyRecord on-the-fly. Modify EdgePropertyProjection.forEdge to
call a new helper (e.g. forEdgeTargeted or forEdgeFromStatePrefix) that uses
requireWarpState(state) and edgeRecordForProjectionTarget(edge) to compute the
prefix, then iterates the underlying store/entries producing only matching
records; keep the existing fromState() intact for full-graph enumeration and
mirror the same change pattern used for NodePropertyProjection.forNode to
maintain symmetry.

In `@src/domain/services/NodePropertyProjection.ts`:
- Around line 38-45: forNode currently builds the entire visible-property
projection via fromState() and then filters, causing wasteful allocations;
change it to a targeted iteration: after resolving checkedState with
requireWarpState and the owner via nodeRecordForProjectionTarget, iterate
checkedState.prop and only process entries whose encoded key begins with
owner.id.toString(), call nodePropertyRecordForOwnerRegister(owner, encodedKey,
register) for matches, collect non-null records, sort with
compareNodePropertyRecords, freeze and return; keep fromState() unchanged for
full-graph enumeration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d9d52abc-6d17-4d71-9a92-2c025de1cb97

📥 Commits

Reviewing files that changed from the base of the PR and between c66f1a4 and dd14007.

📒 Files selected for processing (26)
  • CHANGELOG.md
  • docs/BEARING.md
  • docs/design/0174-v18-post-25-property-projection-runway/v18-post-25-property-projection-runway.md
  • docs/design/0175-v18-legacy-property-projection-nouns/v18-legacy-property-projection-nouns.md
  • docs/design/0176-v18-node-property-projection/v18-node-property-projection.md
  • docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md
  • docs/design/0178-v18-query-property-projection-reads/v18-query-property-projection-reads.md
  • src/domain/graph/LegacyContentPropertyKeys.ts
  • src/domain/graph/LegacyEdgePropertyKey.ts
  • src/domain/graph/LegacyNodePropertyKey.ts
  • src/domain/graph/LegacyPropertyKeyClassification.ts
  • src/domain/graph/LegacyPropertyProjection.ts
  • src/domain/graph/LegacyPropertyValue.ts
  • src/domain/graph/VisibleEdgePropertyRecord.ts
  • src/domain/graph/VisibleNodePropertyRecord.ts
  • src/domain/graph/publicGraphSubstrate.ts
  • src/domain/services/EdgePropertyProjection.ts
  • src/domain/services/KeyCodec.ts
  • src/domain/services/LegacyPropertyProjectionTarget.ts
  • src/domain/services/NodePropertyProjection.ts
  • src/domain/services/controllers/QueryReads.ts
  • src/domain/types/PropValue.ts
  • test/unit/domain/graph/LegacyPropertyProjection.test.ts
  • test/unit/domain/services/EdgePropertyProjection.test.ts
  • test/unit/domain/services/NodePropertyProjection.test.ts
  • test/unit/domain/services/QueryReadsPropertyProjection.test.ts

Comment thread src/domain/services/EdgePropertyProjection.ts
Comment thread src/domain/services/EdgePropertyProjection.ts
Comment thread src/domain/services/NodePropertyProjection.ts
Comment thread src/domain/services/NodePropertyProjection.ts Outdated
@flyingrobots
Copy link
Copy Markdown
Member Author

@codex spot check please: can you review the latest follow-up commits (83223209, b62e72a7, dd14007d) with focus on tolerant public property-query misses, targeted projection reads, shared legacy content key constants, and PropValue class-instance rejection? I am looking for regressions, architecture drift, or missed tests before merge.

@flyingrobots
Copy link
Copy Markdown
Member Author

@coderabbitai Feedback resolution pushed in 1deb8cd9.

Feedback Outcome Commit
NodePropertyProjection tolerant node target could still throw Refactored the target resolver to split runtime-backed targets from string targets, added malformed public target coverage, and kept string lookup behind explicit projection-target validation. 1deb8cd9
EdgePropertyProjection tolerant edge target could still throw Added edgeRecordFromProjectionTarget() so runtime edge records are only built after explicit projection-target validation. 1deb8cd9
Full edge-property projection could throw on malformed edge identifiers Reused the validated edge target path for decoded full-projection key parts and added malformed reserved-endpoint state coverage. Invalid entries now skip instead of aborting projection. 1deb8cd9
Duplicate compareStrings in node/edge projection services Extracted shared compareStrings() to src/domain/utils/StringComparison.ts and imported it from both projection services. 1deb8cd9
Targeted reads should avoid unrelated-owner materialization Added exact encoded owner-key prefix checks before per-entry record construction in forNodeRecord() and forEdgeRecord(). 1deb8cd9
Incorrect design-doc test paths Corrected the node and edge projection verification commands to the actual test/unit/domain/services/*Projection.test.ts paths. 1deb8cd9

Verification:

Gate Result
Focused property projection suite PASS, 15 tests
npm run typecheck PASS
npm run lint PASS
npm run lint:sludge PASS
npm run lint:quarantine-graduate PASS
npm run test:local PASS, 484 files / 6945 tests
markdownlint on touched docs/changelog PASS
git diff --check PASS
Pre-push IRONCLAD M9 PASS

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me verify the key changes from 1deb8cd9 before signing off.


🏁 Script executed:

#!/bin/bash
# Review commit 1deb8cd9 changes
git show --stat 1deb8cd9

echo "=== NodePropertyProjection.ts ==="
cat src/domain/services/NodePropertyProjection.ts

echo "=== EdgePropertyProjection.ts ==="
cat src/domain/services/EdgePropertyProjection.ts

echo "=== StringComparison.ts ==="
fd StringComparison.ts --exec cat {}

echo "=== QueryReads.ts lines 130-200 ==="
sed -n '130,200p' src/domain/services/controllers/QueryReads.ts

echo "=== Test: QueryReadsPropertyProjection.test.ts ==="
cat test/unit/domain/services/QueryReadsPropertyProjection.test.ts

Length of output: 24639


@flyingrobots All six items from the prior review check out against 1deb8cd9. Here is the verification summary:

Feedback item Code verification Result
NodePropertyProjection tolerant target nodeRecordForProjectionTarget() gates on isLegacyNodePropertyProjectionTarget for string inputs before any getNodeRecord call — strict noun construction never reached on malformed carriers
EdgePropertyProjection tolerant target edgeRecordFromProjectionTarget() validates via isLegacyEdgePropertyProjectionTarget before calling EdgeRecord.fromLegacyEdge — both forEdge and the full edgePropertyRecordForRegister path skip malformed entries cleanly
Full edge projection throw on malformed keys edgePropertyKeyPartsFromValues routes through isLegacyEdgePropertyProjectionTarget before returning key parts; invalid decoded entries return null and are skipped
Targeted reads avoid unrelated-owner materialization forNodeRecord uses encodePropKey(owner.id, '') prefix guard; forEdgeRecord uses edgePropertyKeyPrefix(owner) prefix guard — only matching-prefix entries are materialized
Shared compareStrings src/domain/utils/StringComparison.ts is a clean locale-insensitive implementation imported by both projection services
Malformed-miss regression tests QueryReadsPropertyProjection.test.ts covers empty string, NUL bytes, reserved-prefix, and empty-from/to/label edge carriers, all resolving to null

One non-blocking observation: edgePropsFromState in QueryReads.ts still calls EdgeRecord.fromLegacyEdge(edge).id directly after the isLegacyEdgePropertyProjectionTarget guard rather than reusing edgeRecordFromProjectionTarget. Since the guard already validates segment safety, this is not a correctness or safety issue — just a cosmetic redundancy. No action needed before merge.

The implementation is solid. Approving.

[approve]

@github-actions
Copy link
Copy Markdown

Release Preflight

  • package version: 17.0.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v17.0.1, release workflow will publish.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/design/0176-v18-node-property-projection/v18-node-property-projection.md (1)

114-125: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Refresh the verification transcript to match current test output.

The Observed GREEN snippet still shows Tests 2 passed (2), but the current suite in this slice includes additional tests. Please update the captured output so the design record stays accurate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@docs/design/0176-v18-node-property-projection/v18-node-property-projection.md`
around lines 114 - 125, Update the "Observed GREEN" transcript to reflect the
current test run output: re-run the test command for NodePropertyProjection (npx
vitest run test/unit/domain/services/NodePropertyProjection.test.ts
--reporter=verbose) and replace the outdated "Tests  2 passed (2)" line and any
other stale lines with the actual current output from that run, and also ensure
subsequent linter/typecheck commands shown (eslint, npm run typecheck, npm run
lint:sludge) reflect their current statuses for NodePropertyProjection.ts and
NodePropertyProjection.test.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@docs/design/0176-v18-node-property-projection/v18-node-property-projection.md`:
- Around line 114-125: Update the "Observed GREEN" transcript to reflect the
current test run output: re-run the test command for NodePropertyProjection (npx
vitest run test/unit/domain/services/NodePropertyProjection.test.ts
--reporter=verbose) and replace the outdated "Tests  2 passed (2)" line and any
other stale lines with the actual current output from that run, and also ensure
subsequent linter/typecheck commands shown (eslint, npm run typecheck, npm run
lint:sludge) reflect their current statuses for NodePropertyProjection.ts and
NodePropertyProjection.test.ts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a7b0132-b8c4-4555-a52b-3339f17c4b6e

📥 Commits

Reviewing files that changed from the base of the PR and between dd14007 and 1deb8cd.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • docs/design/0176-v18-node-property-projection/v18-node-property-projection.md
  • docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md
  • src/domain/services/EdgePropertyProjection.ts
  • src/domain/services/NodePropertyProjection.ts
  • src/domain/utils/StringComparison.ts
  • test/unit/domain/services/EdgePropertyProjection.test.ts
  • test/unit/domain/services/NodePropertyProjection.test.ts
  • test/unit/domain/services/QueryReadsPropertyProjection.test.ts
✅ Files skipped from review due to trivial changes (3)
  • src/domain/utils/StringComparison.ts
  • CHANGELOG.md
  • docs/design/0177-v18-edge-property-projection/v18-edge-property-projection.md

@flyingrobots
Copy link
Copy Markdown
Member Author

@coderabbitai Follow-up doc feedback resolved in f88e1d25.

Feedback Outcome Commit
docs/design/0176-v18-node-property-projection Observed GREEN transcript had stale Tests 2 passed (2) output Re-ran NodePropertyProjection.test.ts and updated the transcript to Tests 4 passed (4). f88e1d25
Adjacent projection transcript counts were stale after the same follow-up tests Refreshed EdgePropertyProjection and QueryReads property projection transcript counts to the current observed outputs. f88e1d25

Verification for this docs-only follow-up:

Gate Result
npx vitest run test/unit/domain/services/NodePropertyProjection.test.ts --reporter=verbose PASS, 4 tests
npx vitest run test/unit/domain/services/EdgePropertyProjection.test.ts --reporter=verbose PASS, 4 tests
npx vitest run test/unit/domain/services/QueryReadsPropertyProjection.test.ts test/unit/domain/services/NodePropertyProjection.test.ts test/unit/domain/services/EdgePropertyProjection.test.ts --reporter=verbose PASS, 10 tests
npx vitest run test/unit/domain/WarpGraph.query.test.ts test/unit/domain/WarpGraph.edgeProps.test.ts test/unit/domain/WarpGraph.edgePropVisibility.test.ts test/unit/domain/services/QueryReadsPropertyProjection.test.ts --reporter=verbose PASS, 48 tests
markdownlint on touched design docs PASS
git diff --check PASS
Pre-push IRONCLAD M9 PASS

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Tip

For best results, initiate chat on the files or code changes.

@flyingrobots Confirmed — f88e1d25 is a clean docs-only follow-up. Stale transcript counts are now consistent with the actual test suite state (2→4 for NodePropertyProjection, adjacent suites refreshed to match), and the verification sweep covers all the right surfaces: per-suite verbose runs, the cross-suite combined run, markdownlint, diff check, and IRONCLAD M9. Nothing left to flag here.

PR is ready to merge. 🐇 ✨

@github-actions
Copy link
Copy Markdown

Release Preflight

  • package version: 17.0.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v17.0.1, release workflow will publish.

@flyingrobots
Copy link
Copy Markdown
Member Author

@codex Code Lawyer audit activity summary for this branch.

ID Source Severity Surface Finding Action Commit / Ref Outcome
1 CodeRabbit/global PR metadata P5 PR body Required ## ADR checks section from the PR template was missing. Updated the PR description with explicit ADR checks and N/A rationale for untouched persisted-op, wire, and schema-constant surfaces. PR metadata update, no branch commit required Resolved
2 Self audit P5 PR body ## Verification npm run test:local transcript was stale after the suite reached 6945 tests. Re-ran npm run test:local and updated the PR body to 484 files / 6945 tests passed. PR metadata update, no branch commit required Resolved
3 GraphQL review-thread audit P0-P5 PR review threads Full review-thread query returned zero unresolved threads. No code action required. f88e1d251426404a8cdbc1df7c68eabe6c5eaf53 Clean
4 Self audit P0-P5 origin/main...HEAD branch diff No new logic, determinism, TypeScript policy, Markdown, or diff-whitespace issues found in this audit pass. Re-verified with local gates and GitHub checks. f88e1d251426404a8cdbc1df7c68eabe6c5eaf53 Clean

Verification just completed locally:

Gate Result
git status --porcelain Clean
git diff --check origin/main...HEAD Pass
npm run typecheck Pass
npm run lint Pass
npm run lint:sludge Pass
npm run lint:quarantine-graduate Pass
Targeted markdownlint-cli2 over changed docs Pass
npm run test:local 484 files / 6945 tests passed
gh pr checks 99 All reported checks pass

@flyingrobots flyingrobots merged commit 7d6cf66 into main May 23, 2026
9 checks passed
@flyingrobots flyingrobots deleted the v18-continuum-slices-26-30 branch May 23, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant