Skip to content

feat: HC-111 monotonic selector contracts#22

Merged
SoundBlaster merged 14 commits into
mainfrom
feat/hc-111-contracts
Jun 12, 2026
Merged

feat: HC-111 monotonic selector contracts#22
SoundBlaster merged 14 commits into
mainfrom
feat/hc-111-contracts

Conversation

@SoundBlaster

@SoundBlaster SoundBlaster commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds @contract: block syntax to .hcs cascade sheets — selectors declare property constraints (type, required/optional, numeric bounds >= / <=)
  • ContractValidator enforces monotonicity: a more-specific selector may only narrow constraints (HC2101 type mismatch, HC2102 interval widening, HC2103 optional weakening). Pairs are compared only when some node matches both selectors (CSS-style), and type conflicts at equal specificity are caught
  • IR v2 echoes applicable contracts per property in ascending-specificity order; Emitter.emit accepts commands: and contracts:
  • EBNF Syntax 0.2 (grammar) + Resolution 0.2 (§7 contract semantics: accumulation by intersection, omitted bound inherits); RFC 0.2 (contracts in the language model)

Strict-review follow-ups included (R1–R8, R10–R13)

This branch also carries the fixes from the 2026-06-11 strict review (see DOCS/Workplan.md § Review follow-ups):

  • R1 every IR v2 node carries its schema-required hash (child nodes were missing it)
  • R2 no trap on numerals beyond Int.max (1e+26 instead of a crash)
  • R3/R4 contract pairs gated on selector overlap; equal-specificity type conflicts reported
  • R5/R6 core stays Foundation-free (hand-rolled padding, no String(format:)); warning-free build
  • R7 ContractTests on XCTest like the rest of the suite
  • R8 CI validates emitted IR v2 with ajv (caught a oneOfanyOf schema bug); EBNF/RFC/workplan items delivered; contracts[] sorted; 0.5.0-dev comment
  • R10 TypedValue keeps the source lexeme — v1 IR round-trips byte-for-byte (1.10, 0123)
  • R11 SHA-256 via swift-crypto (Linux-capable), D4 superseded
  • R12 intersection semantics for omitted bounds specified normatively
  • R13 public inits for Match/PropertyTrace/NodeTrace; ResolvedValue.init(winner:losers:) derives value/provenance

R9 (value validation against contracts, HC2104) lands as a stacked PR-5.

Test plan

  • swift build — zero warnings
  • 79 tests pass (was 54 before the chain)
  • Live repros from the review re-run: nested hashes (3/3), huge numeral, disjoint selectors clean, equal-specificity conflict caught, v1 lexemes byte-for-byte
  • ajv validate green for dev + production example IR

🤖 Generated with Claude Code

Adds @contract: block syntax to .hcs sheets. Each selector block declares
property constraints (type, required/optional, numeric bounds). ContractValidator
enforces monotonicity: more-specific selectors may only narrow — never widen —
the interval, type, or required status inherited from less-specific ones.

- CascadeSheet: ContractType, PropertyContract, SelectorContract, updated CascadeSheet
- CascadeSheetReader: parses @contract: blocks; [?] optional marker; >= / <= bounds
- ContractValidator: pairwise HC2101/HC2102/HC2103 diagnostics
- Validator: integrates ContractValidator into sheet validation
- Emitter v2: contracts[] field per property; accepts commands + contracts params
- CLI emit: passes commands and sheet.contracts to Emitter
- EBNF/Hypercode_Syntax.md: v0.2 — .hcs and @contract: grammar documented
- 14 new tests (Contract parsing, ContractValidator, Validator integration); 74 total

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 22:44

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69a38f03c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/Hypercode/Validation/ContractValidator.swift
Comment thread Sources/Hypercode/Emit/Emitter.swift Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces HC-111 “monotonic selector contracts” for .hcs cascade sheets, wires contract data through IR v2 emission, and adds supporting infrastructure (typed scalar values, cascade trace retention, hashing, and an explain CLI command) to make contract enforcement and debugging observable.

Changes:

  • Add .hcs @contract: parsing plus ContractValidator monotonicity enforcement (HC2101/HC2102/HC2103) and integrate into Validator.
  • Extend resolver + IR emission to support typed values, winner/losers trace, IR v2 output (hashes, context echo, contracts[]), and CLI --ir-version.
  • Add hypercode explain implementation and tests; update schema + grammar/workplan docs.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
Tests/HypercodeTests/WhiteLabelTests.swift Update assertions for typed scalar values (TypedValue).
Tests/HypercodeTests/SHA256Tests.swift Add SHA-256 test vectors for hashing correctness.
Tests/HypercodeTests/HCSReaderTests.swift Update reader tests for typed parsing.
Tests/HypercodeTests/ExplainTests.swift Add end-to-end tests for cascade trace via Explainer.
Tests/HypercodeTests/EmitterTests.swift Add IR v2 structure/hash determinism tests.
Tests/HypercodeTests/ContractTests.swift Add contract parsing + monotonicity + validator integration tests.
Tests/HypercodeTests/CascadeResolverTests.swift Update typed assertions + verify losers retention.
Sources/HypercodeCLI/main.swift Add explain subcommand; add --ir-version to emit; pass .hcs file for provenance.
Sources/Hypercode/Validation/Validator.swift Run contract checks as part of .hcs validation.
Sources/Hypercode/Validation/ContractValidator.swift New monotonicity validator for selector contracts.
Sources/Hypercode/HCS/Resolver.swift Track provenance file; retain winner/losers for explain + IR v2.
Sources/Hypercode/HCS/CascadeSheetReader.swift Add @contract: parsing; typed scalar inference; expose selector parsing for CLI.
Sources/Hypercode/HCS/CascadeSheet.swift Add TypedValue, Match, and contract model types; extend CascadeSheet.
Sources/Hypercode/Explain/Explainer.swift New resolved-tree walker to build per-node property traces and render text.
Sources/Hypercode/Emit/Emitter.swift Add IR v2 emission, typed encoding, per-node/document hashing, and version selection.
Sources/Hypercode/Crypto/SHA256.swift Add CryptoKit-based SHA-256 wrapper used by IR hashing.
Schema/hypercode-ir-v2.schema.json Add/define IR v2 schema including contracts[]/trace/hash fields.
EBNF/Hypercode_Syntax.md Update grammar doc to v0.2 and document .hcs + @contract: syntax.
DOCS/Workplan.md Add detailed implementation plan for HC-110..112/111 sequence.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/Hypercode/Validation/ContractValidator.swift
Comment thread Sources/Hypercode/Emit/Emitter.swift Outdated
Comment thread Sources/Hypercode/Emit/Emitter.swift Outdated
Comment thread Sources/Hypercode/Emit/Emitter.swift Outdated
Comment thread Schema/hypercode-ir-v2.schema.json
Comment thread DOCS/Workplan.md Outdated
Comment thread Schema/hypercode-ir-v2.schema.json
Comment thread DOCS/Workplan.md
Comment thread Sources/Hypercode/Emit/Emitter.swift
SoundBlaster and others added 10 commits June 11, 2026 11:28
- New "Review follow-ups" section: R1-R8 blocking PR #22 (child-node hash
  schema violation, emitter Int-overflow crash, contract validator false
  positives and equal-specificity gap, Foundation leak in Explainer, compiler
  warning, test-framework consistency, undelivered plan items), R9-R13 pending
  owner decisions (contract value validation, lossy v1, D4/CryptoKit deviation,
  absent-bound semantics, public API construction gaps)
- Fix PR-4 header: #22 is open, not merged; link PR numbers on all four headers
- Annotate D4 decision row with the CryptoKit deviation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- R9: contract value validation -> separate PR-5 with HC2104; files table fixed
- R10: store source lexeme alongside typed value; v1 emits byte-for-byte
- R11: switch SHA-256 to swift-crypto (D4 superseded; Linux-capable)
- R12: omitted bound = inheritance via interval intersection; specify in RFC
- R13: public inits for Match/PropertyTrace/NodeTrace + derived ResolvedValue init

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Package.swift: swift-crypto dependency (Linux-capable, same CryptoKit API);
  0.5.0-dev version comment (R8)
- SHA256.swift: import Crypto; hand-rolled hexString (String(format:) was a
  Foundation leak via member lookup)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
R10: TypedValue is now a struct {kind, lexeme} — the typed kind drives v2 IR
and hashing, the source lexeme drives v1 IR and explain text, so v1 round-trips
byte-for-byte ("1.10" stays "1.10", "0123" stays "0123"). Static factories keep
existing .string()/.int() call sites compiling unchanged.

R13: explicit public inits for Match, PropertyTrace, NodeTrace;
ResolvedValue.init(winner:losers:) derives value/provenance from winner —
an inconsistent instance can no longer be constructed. PropertyCascade.decide
simplified accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
R1: nodeV2 now returns (ir, hash) recursively — child hashes are computed
once and embedded, replacing the root-only zip-insert that violated the
schema's required "hash" on every resolvedNode. Command arrays are padded,
never zip-truncated, so a caller-side mismatch cannot drop nodes.

R2: whole-number doubles render via Int(exactly:) and a finiteness guard —
a 26-digit numeral emits as 1e+26 instead of trapping in Int(_:).

Also (R8): contracts[] accumulate in ascending specificity with declaration
order as the stable tie-breaker. Per-node contract matching hoisted out of
the per-property loop. 3 regression tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ty conflicts

R3: like the CSS cascade, specificity only relates contracts that can govern
the same node — a pair is checked only when some node in the document matches
both selectors. Kills the false-positive HC2102 on disjoint selectors that
made legitimate sheets unwritable. ContractValidator.validate now takes the
forest; Validator passes it through.

R4: contracts at equal specificity apply with equal force — a type conflict
makes the intersection unsatisfiable and is now reported as HC2101 (was
silently skipped by the strict `<` guard). Bounds at equal specificity
intersect and stay legal.

R7 (bundled — same file rewrite): ContractTests converted from Swift Testing
to XCTest, matching the other 14 test files. Shared-key iteration is sorted
for deterministic diagnostic order. 79 tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
padding(toLength:withPad:startingAt:) is Foundation/NSString API that compiled
only via Swift 5 leaky member lookup (no import in file; Foundation loaded
transitively) and would break under MemberImportVisibility. Replaced with
hand-rolled padding — the core target stays Foundation-free. Also fixes the
never-mutated `var line1` warning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EBNF/Hypercode_Resolution.md 0.2: new §7 Contracts — accumulation by
  intersection (omitted bound inherits, decision R12), overlap-gated
  monotonicity with HC2101-HC2103 table, HC2104 planned as PR-5, IR echo
  ordering; "typed scalars" un-deferred (landed with IR v2)
- EBNF/Hypercode_Syntax.md: <scalar> grammar fixed — bare unquoted strings
  are valid (driver: sqlite); type-inference and lexeme preservation noted;
  stale Date fixed; semantics table moved out (syntax doc covers syntax only)
- RFC/Hypercode.md 0.2: contracts in the language model (§9.4 refined with
  intersection + overlap rules), explain and IR v2 marked shipped (§9.4,
  §9.7), type-system maturity limitation updated (§9.8), changelog entry
- workplan.md M8: HC-110/111/112 annotated with open PR links (boxes get
  checked on merge)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…schema

The PR-2 plan promised an ajv validation step — now the Swift workflow emits
IR v2 from Examples/service.{hc,hcs} (dev + production contexts) and validates
it with ajv (draft 2020-12).

Wiring this up immediately caught a schema bug the review missed: typed
values used oneOf [string|integer|number|boolean], and every int value
matches both "integer" and "number", failing "exactly one". Switched to
anyOf in resolvedProperty.value and matchEntry.value. Both example outputs
now validate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SoundBlaster and others added 2 commits June 12, 2026 00:47
- HypercodeVersion.current is the single source of truth for resolver.version
  in IR v2 (was a hardcoded "0.5.0" drifting from CHANGELOG's 0.4.0); set to
  0.5.0-dev until release
- Schema descriptions no longer claim contracts[] is "empty until HC-111" —
  documented as accumulated ascending-specificity contracts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@SoundBlaster SoundBlaster changed the base branch from main to feat/hc-110-explain June 12, 2026 13:00
@SoundBlaster SoundBlaster changed the base branch from feat/hc-110-explain to main June 12, 2026 13:52
@SoundBlaster SoundBlaster merged commit fa236eb into main Jun 12, 2026
5 checks passed
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.

2 participants