Skip to content
Merged
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
[Semantic Versioning](https://semver.org/).

## [Unreleased] — 0.6.0-dev

### Added
- `@import "path.hcs"` for cascade sheets (HC-116): depth-first expansion at
the directive position (the importer wins specificity ties), import-once
for diamonds, cycle detection, cross-file provenance; contracts compose
across imports. `ImportHandling` API (`.unsupported`/`.syntaxOnly`/`.loader`).

## [0.5.0] — 2026-06-12

Spec-layer hardening: the resolver becomes a verifiable pipeline — typed IR
Expand Down
51 changes: 50 additions & 1 deletion DOCS/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ $ hypercode emit Examples/service.hc --hcs Examples/service.hcs --ctx env=produc
{
"version": "hypercode.ir/v2",
"context": { "env": "production" }, // echo of --ctx
"resolver": { "name": "hypercode-swift", "version": "0.5.0" },
"resolver": { "name": "hypercode-swift", "version": "0.6.0-dev" },
"documentHash": "3be098179523f21c…",
"nodes": [ /* … each node: */
{
Expand Down Expand Up @@ -243,6 +243,55 @@ $ echo $?
(unreadable or non-v2 input) — usable as a CI gate
("spec changed → require regeneration").

## 7. Sheet modularity: `@import`

Real configurations don't live in one file (HC-116). A tenant sheet starts
from the product baseline and overrides only what differs
([`Examples/imports/acme.hcs`](../Examples/imports/acme.hcs)):

```hcs
@import "../service.hcs"

'#main-db':
pool_size: 80

APIServer > Listen:
port: 8443
```

Imports expand at the directive position, so the importer's rules come later
in source order and win specificity ties. `explain` shows the cross-file
cascade — every rule keeps the file it was defined in:

```console
$ hypercode explain Examples/service.hc --hcs Examples/imports/acme.hcs \
--ctx env=production "APIServer > Listen" port
Node: Service > APIServer > Listen
port
WINNER APIServer > Listen { value: 8443 }
file: Examples/imports/acme.hcs line: 10 specificity: (0,0,2) order: 9
────────────────────
losing APIServer > Listen { value: 8080 }
file: Examples/service.hcs line: 26 specificity: (0,0,2) order: 7
losing APIServer > Listen { value: 5000 }
file: Examples/service.hcs line: 11 specificity: (0,0,2) order: 3
```

The baseline's `@contract:` block still gates the tenant — contracts compose
across imports and only narrow:

```console
$ hypercode validate Examples/service.hc --hcs bad-tenant.hcs --ctx env=production
bad-tenant.hcs:3:1: error[HC2104]: contract violation for 'port': 99999 exceeds upper bound 65535.0 from contract 'APIServer > Listen'
```

Rules of the road: imports go first; paths resolve relative to the importing
sheet; a sheet expands once per resolution (diamonds are fine); a cycle is an
error naming the chain. Trust model: the CLI loader reads any path the
process can read (absolute and `../` included) — fine for local trusted
config; embedders resolving untrusted sheets supply a policy-bound loader
(RFC §8).

## Scalar typing cheat-sheet

| Written in `.hcs` | Resolved as |
Expand Down
24 changes: 22 additions & 2 deletions EBNF/Hypercode_Resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

**Status:** Draft

**Version:** 0.2
**Version:** 0.3

**Date:** June 11, 2026
**Date:** June 12, 2026

**Author:** Egor Merkushev

Expand Down Expand Up @@ -91,6 +91,26 @@ Because `order` is unique per rule, `max` is unambiguous: higher specificity
wins, and equal specificity is broken by later source order. (`PropertyCascade`
is the `DecisionSpec` that performs this choice.)

### 5.1 Sheet composition (`@import`, HC-116)

The rule sequence the cascade operates on may be composed from several sheets:

```text
rules(sheet) = expand(imports(sheet)) ++ ownRules(sheet)
```

- An imported sheet expands **depth-first at the position of its directive**;
`order` is assigned over the fully expanded sequence. Imports must precede
all rules, so the importing sheet's own rules always come later in source
order and win specificity ties — the importer overrides what it imports.
- Each sheet expands **at most once** per resolution (a diamond keeps the
first occurrence); a cyclic import is an error.
- Contracts compose by the same expansion and then accumulate as in §7 —
a contract declared in an imported sheet still gates values set by the
importer.
- `provenance(rule)` keeps the file the rule was **defined** in, not the
entry sheet.

## 6. Resolution

```text
Expand Down
27 changes: 23 additions & 4 deletions EBNF/Hypercode_Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

**Status:** Draft

**Version:** 0.2
**Version:** 0.3

**Date:** June 11, 2026
**Date:** June 12, 2026

**Author:** Egor Merkushev

Expand Down Expand Up @@ -110,12 +110,14 @@ Root

## 6. `.hcs` Cascade Sheet Syntax

A `.hcs` file contains cascade rules and optional contract blocks.
A `.hcs` file contains optional import directives, cascade rules and optional
contract blocks.

### 6.1 Cascade Rule

```
<sheet> ::= { <top-level-block> }
<sheet> ::= { <import-stmt> } { <top-level-block> }
<import-stmt> ::= "@import" <ws> <quoted-string> <newline>
<top-level-block> ::= <dimension-block> | <contract-block> | <rule-block>

<dimension-block> ::= "@" <identifier> "[" <value> "]" ":" <newline>
Expand All @@ -135,6 +137,17 @@ form → int, decimal form without letters → double, anything else → string
(e.g. `driver: sqlite` is a valid bare string). Quoting forces string. The
source lexeme is preserved verbatim for v1 IR round-tripping.

#### `@import` (HC-116)

`@import "path.hcs"` includes another sheet. All imports must precede rules,
context blocks and contracts (CSS discipline). An imported sheet expands
depth-first **at the position of the directive**, so the importing sheet's
own rules come later in source order and win specificity ties — the importer
overrides what it imports. Each sheet is expanded at most once per resolution
(diamond imports are deduplicated); a cyclic import is an error. Every rule
keeps the file it was defined in for provenance. Paths resolve relative to
the importing sheet's location.

### 6.2 Selectors

```
Expand Down Expand Up @@ -192,6 +205,12 @@ only.

## 8. Change Log

**Version 0.3** (2026-06-12)

* Added `@import` directive grammar (HC-116): `<import-stmt>` precedes all
blocks; expansion order, import-once and cycle semantics summarized here,
defined normatively in `Hypercode_Resolution.md` §5.1.

**Version 0.2** (2026-06-11)

* Added Section 6: `.hcs` cascade sheet syntax (rules, selectors, dimension blocks).
Expand Down
11 changes: 11 additions & 0 deletions Examples/imports/acme.hcs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Tenant sheet (HC-116): start from the product baseline, override only what
# differs for this tenant. Imported rules come first in source order, so the
# overrides below win specificity ties; the baseline's @contract: block still
# gates every value set here.
@import "../service.hcs"

'#main-db':
pool_size: 80

APIServer > Listen:
port: 8443
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// swift-tools-version: 5.9
import PackageDescription

// Package version: 0.5.0 (echoed by the IR v2 emitter as resolver.version)
// Package version: 0.6.0-dev (echoed by the IR v2 emitter as resolver.version)
let package = Package(
name: "Hypercode",
platforms: [
Expand Down
26 changes: 25 additions & 1 deletion RFC/Hypercode.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Status:** Draft

**Version:** 0.2
**Version:** 0.3

**Date:** June 11, 2026

Expand Down Expand Up @@ -152,6 +152,21 @@ The HCS resolution process follows a strict order of precedence, analogous to CS

When multiple rules match a single command, their properties are merged. Properties from higher-specificity rules override those from lower-specificity rules.

#### 4.2.4. Sheet Composition (`@import`)

Real configurations do not live in one file. A sheet MAY begin with one or
more `@import "path.hcs"` directives; all imports MUST precede rules, context
blocks and contracts. An imported sheet expands depth-first at the position of
its directive, so the importing sheet's own rules come later in source order
and win specificity ties — the importer overrides what it imports, which gives
the "user override file" origin from §4.2.3 concrete semantics without an
`!important` mechanism. Each sheet expands at most once per resolution
(diamonds are deduplicated); a cyclic import is an error. Contracts compose by
the same expansion and still only narrow: a contract declared in an imported
baseline gates every value the importer sets. Provenance keeps the file each
rule was defined in, so `explain` and the IR point at the real source across
files. (Normative semantics: `EBNF/Hypercode_Resolution.md` §5.1.)

## 5. Example: A Simple Web Service

This example demonstrates how a single Hypercode file can be configured for both development and production environments using an HCS file.
Expand Down Expand Up @@ -236,6 +251,8 @@ Hypercode and HCS are declarative and do not define runtime execution isolation

The specification assumes that the resolution and execution engine is trusted. No mechanisms are currently defined for verifying integrity of `.hcs` rules or controlling their provenance. Future versions may include digital signing or validation capabilities.

`@import` (§4.2.4) widens the read surface: resolving a sheet now reads every transitively imported file. **The import loader is the policy boundary.** The reference CLI loader resolves any path the process can read — including absolute and `../` targets — which is appropriate for trusted local configuration. Embedding contexts that resolve untrusted sheets (agents, services) MUST supply a policy-bound loader (root-confined paths, allow-lists) instead; the `ImportHandling.loader` API exists precisely so that policy lives with the embedder.

## 9. Novelty and Prior Art

### 9.1. The Claim
Expand Down Expand Up @@ -335,6 +352,13 @@ Prior art surveyed in §9:

## 12. Change Log

**Version 0.3** (2026-06-12):

* Sheet composition via `@import` (HC-116, §4.2.4): depth-first expansion at
the directive position, importer wins specificity ties, import-once,
cycle detection, cross-file provenance. Normative semantics in
`EBNF/Hypercode_Resolution.md` §5.1.

**Version 0.2** (2026-06-11):

* Contracts are now part of the language model (HC-111): `@contract:` block syntax in `.hcs`, accumulation by intersection (an omitted bound inherits), specificity relating only contracts that can govern the same node, monotonicity diagnostics HC2101–HC2103 (§9.4); value-level enforcement (HC2104) declared in progress.
Expand Down
4 changes: 3 additions & 1 deletion Sources/Hypercode/Diagnostics/DocumentDiagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public func diagnostics(for kind: DocumentKind, text: String, file: String? = ni
do {
switch kind {
case .cascadeSheet:
_ = try CascadeSheetReader().read(text)
// A lone text buffer can't load other files — @import directives
// are syntax-checked here; expansion happens in the CLI/library.
_ = try CascadeSheetReader().read(text, imports: .syntaxOnly)
return []
case .hypercode:
let forest = try Parser(source: text).parse()
Expand Down
1 change: 1 addition & 0 deletions Sources/Hypercode/Documentation.docc/Hypercode.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ hypercode diff old.ir.json new.ir.json
- ``Rule``
- ``ContextGuard``
- ``CascadeSheetReader``
- ``ImportHandling``
- ``Resolver``
- ``ResolutionContext``
- ``ResolvedNode``
Expand Down
Loading
Loading