Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 135 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
# ── IRONCLAD M9 static firewall ──────────────────────────────────────────
# This job is the primary fast gate. It runs the static quality checks
# that should fail quickly before heavier runtime matrix jobs start.
# It MUST pass before any PR can merge. Configure as a required status check
# in GitHub branch protection settings. Security audit stays advisory here so
# CI keeps a single authoritative gate instead of duplicating lint/type work
# in a second job.
type-firewall:
# The `type-firewall` job below is the required aggregate status check. Its
# child jobs run the static gates in parallel so PRs wait for the slowest
# static lane instead of the sum of every static lane.
type-firewall-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
Expand All @@ -32,16 +34,55 @@ jobs:
run: npm run typecheck:policy
- name: 'Gate 3: Consumer type surface test'
run: npm run typecheck:consumer

type-firewall-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 4: ESLint (typed rules + no-explicit-any)'
run: npm run lint
- name: 'Gate 4b: Lint ratchet (zero-error invariant)'
run: npm run lint:ratchet
- name: 'Gate 4c: Anti-sludge shell checks (junk-drawer filenames)'
run: npm run lint:sludge
Comment thread
flyingrobots marked this conversation as resolved.

type-firewall-semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 4d: Install semgrep'
run: python -m pip install --upgrade pip semgrep
- name: 'Gate 4e: Semgrep anti-sludge with rule-scoped quarantines'
run: npm run lint:semgrep

type-firewall-quarantine:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 4f: Contamination map matches checked-in manifests (no stale quarantines)'
run: |
npm run lint:contamination
Expand All @@ -55,23 +96,91 @@ jobs:
env:
GIT_WARP_QUARANTINE_BASE: origin/main
run: npm run lint:quarantine-graduate

type-firewall-surface:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 5: Declaration surface validator (manifest vs index.d.ts vs index.js)'
run: npm run typecheck:surface

type-firewall-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 6: Markdown lint (fenced code blocks require language)'
run: npm run lint:md
- name: 'Gate 7: Markdown JS/TS code-sample syntax check'
run: npm run lint:md:code

type-firewall-audit-advisory:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: 'Gate 8: Security audit (runtime deps, advisory)'
continue-on-error: true
run: npm audit --omit=dev --audit-level=high

type-firewall:
runs-on: ubuntu-latest
needs:
- type-firewall-types
- type-firewall-lint
- type-firewall-semgrep
- type-firewall-quarantine
- type-firewall-surface
- type-firewall-docs
if: ${{ always() }}
steps:
- name: 'IRONCLAD M9 aggregate gate'
run: |
echo "types: ${{ needs['type-firewall-types'].result }}"
echo "lint: ${{ needs['type-firewall-lint'].result }}"
echo "semgrep: ${{ needs['type-firewall-semgrep'].result }}"
echo "quarantine: ${{ needs['type-firewall-quarantine'].result }}"
echo "surface: ${{ needs['type-firewall-surface'].result }}"
echo "docs: ${{ needs['type-firewall-docs'].result }}"

test "${{ needs['type-firewall-types'].result }}" = "success"
test "${{ needs['type-firewall-lint'].result }}" = "success"
test "${{ needs['type-firewall-semgrep'].result }}" = "success"
test "${{ needs['type-firewall-quarantine'].result }}" = "success"
test "${{ needs['type-firewall-surface'].result }}" = "success"
test "${{ needs['type-firewall-docs'].result }}" = "success"

typecheck-test-advisory:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
Expand All @@ -85,9 +194,11 @@ jobs:
matrix:
node: [22]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '${{ matrix.node }}'
cache: 'npm'
Expand All @@ -108,23 +219,29 @@ jobs:
test-bun:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Run Bun integration tests
run: docker compose -f docker/docker-compose.test.yml run --rm test-bun

test-deno:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Run Deno integration tests
run: docker compose -f docker/docker-compose.test.yml run --rm test-deno

coverage-threshold:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Use Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: '22'
cache: 'npm'
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- V18 property projection closeout now records the remaining raw
legacy-property boundaries as compatibility, serialization, replay,
reducer, index, or migration-source boundaries before graph-model migration
work begins.
- V18 graph-op algebra projection now emits typed content, node-property, and
edge-property operation nouns instead of exposing legacy property-map entries
as graph substrate operations.
- V18 generic property writes now construct runtime-backed node and edge
property write intent nouns before lowering to the existing legacy
compatibility operation shape.
- V18 state-reader property and content views now route through typed
projection records instead of decoding raw legacy property keys directly.
- V18 query reads now route linear node property reads, edge property reads,
and edge-list property payloads through projection-backed compatibility
records instead of decoding raw property keys in the query controller.
Expand Down Expand Up @@ -70,6 +82,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- V18 property projection review follow-up now removes newly introduced
helper-level `unknown` suppressions from `PatchBuilder` property-value
validation and refreshes graph-op projection docs and public substrate export
ordering.
- V18 PR feedback follow-up now hardens CI checkout permissions and credential
handling, rejects cyclic and prototype-polluting property values before write
intent lowering, covers snapshot-backed state-reader parity, and avoids a
duplicate node-property projection pass during reader context construction.
- CI now runs the heavy `type-firewall` static gates as parallel child jobs
behind a small required aggregate status, so TypeScript, ESLint, Semgrep,
quarantine, declaration-surface, and Markdown checks no longer serialize
behind one long-running job.
- V18 property projection closeout now routes `StateQueryReadModel.nodeProps`,
translation-cost property-key accounting, and public property counts through
property projection records so malformed compatibility keys cannot leak into
live read-model property bags.
- V18 property projection closeout now preserves state-reader support for
immutable `SnapshotWarpState` sources and preserves PatchBuilder
reserved-byte validation errors before property write intent construction.
- V18 property projection review follow-up now preserves tolerant public
property-query misses, scopes targeted projection materialization to requested
owners, skips malformed edge-property projection entries, shares legacy
Expand Down
61 changes: 42 additions & 19 deletions docs/BEARING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ of handwritten adapter folklore.

Current branch state at this boundary:

- Branch: `main`
- Branch: `v18-continuum-slices-31-35`
- Base branch: `main`
- Current `origin/main`: `7d6cf669`
- Latest merged PR: #99, v18 property projection read surface
- Current `origin/main`: `35e5a6a9`
- Latest merged PR: #100, post-PR-99 BEARING cleanup checkpoint
- Latest released package line: `17.0.1`
- Latest completed implementation cycle:
`0178-v18-query-property-projection-reads`
- Current work: cleaned-up post-PR-99 boundary on `main`; next
implementation branch should start at slice 31.
- Cleanup checkpoint: before this signpost update branch, there were no open
PRs and remote refs had been pruned to `origin/main`.
`0183-v18-property-projection-closeout`
- Current work: v18 slices 31 through 35 are implemented on this branch and
ready for PR verification.
- Cleanup checkpoint: before this slice branch, there were no open PRs and
remote refs had been pruned to `origin/main`.

The current v18 graph-model posture is:

Expand All @@ -65,10 +65,19 @@ The current v18 graph-model posture is:
- Runtime-backed legacy property projection nouns exist.
- Node and edge property projections exist.
- Public query property reads use projection-backed compatibility records.
- State-reader property and content views use projection-backed compatibility
records.
- Generic node and edge property writes construct runtime-backed write intents
before lowering to legacy compatibility operations.
- Graph-op algebra projection emits typed content and property operation
nouns, not raw property-map entries.
- Query read-model node props, translation-cost property-key accounting, and
public property counts use property projection nouns.

That is useful progress, not a finish line. The repo still needs property
projection beyond query reads, graph-model migration tooling, and genesis
replay equivalence before v18 can make stronger compatibility claims.
projection beyond replay/serialization boundaries, graph-model migration
tooling, and genesis replay equivalence before v18 can make stronger
compatibility claims.

## What Just Shipped

Expand Down Expand Up @@ -101,15 +110,29 @@ PR #99 landed v18 slices 26 through 30:
malformed-record skipping, shared legacy content keys, and plain-object
property carrier guards.

This branch implements v18 slices 31 through 35:

- state-reader node, edge, and content property views route through typed
projections;
- runtime-backed node and edge property write intent nouns exist;
- `PatchBuilder` generic property writes lower through those intents while
preserving the existing patch wire shape;
- graph-op algebra projection emits typed content and property operation
nouns;
- closeout routed the remaining live read-model property views through
projections and documented the remaining raw legacy-property boundaries.

## What Feels Wrong

- Some non-query read surfaces still have direct raw legacy property
interpretation, especially state-reader context code.
- Generic property writes still lower directly to legacy property operations;
content writes are intent-backed, but property writes are not.
- Content persistence still uses legacy `_content*` compatibility properties.
Typed reads and writes exist over that plane, but the storage cutover is not
complete.
- Temporal replay still extracts node snapshots from the raw legacy property
map because historical replay tests carry pre-codec inline fixture classes
that are not `PropValue`-honest enough for `LegacyPropertyValue`.
- Checkpoint, serializer, state-diff, visible-scope, logical-index,
reducer/op-strategy, and content-projection code still touch the raw
property map as named compatibility or migration boundaries.
- The v18 migration tool does not exist yet. Starting with a write-capable
script would be reckless; the next migration work must be dry-run first.
- Genesis replay equivalence has not been proven. Migration cannot be trusted
Expand Down Expand Up @@ -191,15 +214,15 @@ and concrete checks live in `docs/invariants/`.
[0177](design/0177-v18-edge-property-projection/v18-edge-property-projection.md).
- [x] 30. Route query property reads through projection:
[0178](design/0178-v18-query-property-projection-reads/v18-query-property-projection-reads.md).
- [ ] 31. Route state-reader property views through projection:
- [x] 31. Route state-reader property views through projection:
[0179](design/0179-v18-state-reader-property-projection/v18-state-reader-property-projection.md).
- [ ] 32. Add property write intent nouns:
- [x] 32. Add property write intent nouns:
[0180](design/0180-v18-property-write-intent-nouns/v18-property-write-intent-nouns.md).
- [ ] 33. Route PatchBuilder property writes through intent lowering:
- [x] 33. Route PatchBuilder property writes through intent lowering:
[0181](design/0181-v18-patchbuilder-property-intent-lowering/v18-patchbuilder-property-intent-lowering.md).
- [ ] 34. Cut graph-op algebra over to property projections:
- [x] 34. Cut graph-op algebra over to property projections:
[0182](design/0182-v18-graph-op-projection-property-cutover/v18-graph-op-projection-property-cutover.md).
- [ ] 35. Close out legacy-property projection with evidence:
- [x] 35. Close out legacy-property projection with evidence:
[0183](design/0183-v18-property-projection-closeout/v18-property-projection-closeout.md).
- [ ] 36. Add graph-model migration manifest nouns:
[0184](design/0184-v18-graph-model-migration-manifest/v18-graph-model-migration-manifest.md).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
cycle: 0179
task_id: V18_state_reader_property_projection
status: Planned
status: Complete
sponsors:
human: James
agent: Codex
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
cycle: 0180
task_id: V18_property_write_intent_nouns
status: Planned
status: Complete
sponsors:
human: James
agent: Codex
Expand Down
Loading
Loading