Skip to content

0.10.0 — five declarations, each added because its absence was a wrong answer - #25

Merged
Sunrisepeak merged 5 commits into
mainfrom
declarations-0.10
Aug 30, 2026
Merged

0.10.0 — five declarations, each added because its absence was a wrong answer#25
Sunrisepeak merged 5 commits into
mainfrom
declarations-0.10

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Aug 30, 2026

Copy link
Copy Markdown
Member

Closes #22, #23, #24.

⭐⭐ What the five have in common

Not one of them was a facility nobody had asked for. Each was reached by a C
library above this interface, found to have nothing here to reach, and answered
anyway
— wrongly, and without telling its caller.

A specification that has no operation for X does not cause consumers not to do X.
It causes them to do X badly, out of sight.

declaration what its absence produced
kal_fs_lock / kal_fs_unlock fcntl(F_SETLK) returned success and took no lock — two programs held one exclusive lock and neither could find out
kal_process_spawn_bound kill on a composed execve reached the waiting copy; the caller was told the program died while it ran to completion, unsupervised
kal_task_parallelism hardware_concurrency() answered 1, with no error
kal_fs_set_modified_at no route to a directory's time at all, so an implementation reached one outside anything stated here
kal_fs_capacity std::filesystem::space had nothing beneath it

Each measured against a host as control, not reasoned.

⭐ The lock entry is the opposite of the permission entry

Clause 11 now has them next to each other. Permission was declined because the
environments do not agree that an identity exists. Every environment this
specification targets locks a byte range and spells it almost identically. What
was missing was a word, not a capability.

And release upon the holder's end is required rather than observed — which is
why this cannot be composed above the line. A caller can build exclusion out of
KAL_OPEN_EXCLUSIVE and a name; nothing then releases that name when its holder
dies.

⚠️ The holder is the kal_file, not the program. One environment's oldest
form releases every lock a program holds on a node as soon as it closes any
descriptor for it, so a library that opened one file twice destroyed its own
lock. An implementation shall not expose that.

⚠️ What this deliberately does not do

The two spawn additions do not combine. There is no form that both grants
directories and binds a lifetime; declaring every combination is how an interface
acquires four spawns and then eight. A caller must not meanwhile take
kal_process_spawn_with and assume the binding.

Conformance

  • Clause 8: five declarations added, none altered, no layout touched.
  • Clause 6.2: each operation that a resource may not support is gated by a
    kal_fs_props / kal_process_props position — exactly the argument clause 11
    already records for the link operations.
  • SURFACE.txt, the three modules and conformance/src/declarations.c all carry
    the new names; check-declarations reports 101 entities on both sides.

The rest of the graph moves with it

openkal is the shared dependency of every package, and a version requirement
here is exact. An implementation left behind is not on its own older contract —
nothing resolves at all. Branch declarations-0.10 exists in all five
implementations so this job builds against them:

openkal-linux 0.9.0 all five implemented
openkal-macos 0.7.0 four; spawn_bound refused — no primitive arms it from inside the started image
openkal-windows 0.5.0 four; spawn_bound recorded as next, not claimed until measured
openkal-opensbi 0.4.0 repin — provides no openkal.fs
openkal-uefi 0.4.0 repin

…ong answer

Closes #22, #23, #24.

⭐⭐ WHAT THE FIVE HAVE IN COMMON. Not one of them was a facility nobody had
asked for. Each was reached by a C library above this interface, found to have
nothing here to reach, and answered ANYWAY --- wrongly, and without telling its
caller. A specification that has no operation for X does not cause consumers not
to do X. It causes them to do X badly, out of sight.

`kal_fs_lock' / `kal_fs_unlock' + KAL_FS_PROP_LOCKS
    openkal-musl answered `fcntl(F_SETLK)' with success and took no lock: two
    programs held one exclusive lock and neither could find out. Measured
    against a host.

    ⭐ AND THIS IS THE OPPOSITE OF THE PERMISSION ENTRY, which is why clause 11
    now has them next to each other. Permission was declined because the
    environments do not agree that an identity exists. Every environment this
    specification targets locks a byte range and spells it almost identically.
    What was missing was a word, not a capability.

    ⭐ Release upon the holder's end is REQUIRED rather than observed, and that
    requirement is why this cannot be composed above the line: a caller can build
    exclusion out of KAL_OPEN_EXCLUSIVE and a name, and nothing then releases that
    name when its holder dies.

    ⚠️ The holder is the `kal_file' and NOT the program. One environment's oldest
    form releases every lock a program holds upon a node as soon as it closes ANY
    descriptor for it --- so a library that opened one file twice destroyed its own
    lock. An implementation shall not expose that; the environments that have the
    older form also have a newer one whose holder is exactly the open file.

`kal_process_spawn_bound' + KAL_PROCESS_PROP_BOUND_LIFETIME
    Clause 7.1 declines to replace a running image, and that stands. The
    consequence this specification had NOT recorded is that a C library asked for
    `execve' composes it, and the composition leaves THREE images where a system
    with the operation has two --- and a signal reaches the middle one. Measured
    with a host as control: identical status words, opposite outcomes; the caller
    is told the program died on the signal it sent while the program runs to
    completion, unsupervised.

    `kal_process_terminate' was not at fault. What was missing was a way to SAY
    the thing `execve' means.

`kal_task_parallelism'
    KAL_TASK_PROP_PARALLEL says WHETHER and not HOW MANY, so `hardware_concurrency'
    answered 1 with no error and a program sizing a pool of workers got one worker.
    ⭐ Zero is "cannot say" and is distinct from one: an environment with one
    processor and an environment that will not answer call for different behaviour.

`kal_fs_set_modified_at'
    `kal_fs_set_modified' is stated on an open FILE and a directory is opened as a
    `kal_dir', so this interface had NO route to a directory's time at all --- while
    `kal_fs_info' reports one. A consumer that stamps a lock directory drove one
    implementation to open the directory for READING and set the time on that,
    outside anything stated here. ⚠️ A divergence caused by a missing declaration
    is a defect of the specification, and is recorded as one.

`kal_fs_capacity' + KAL_FS_PROP_CAPACITY
    `std::filesystem::space' had nothing beneath it.

--- what this does NOT do --------------------------------------------------

⚠️ THE TWO SPAWN ADDITIONS DO NOT COMBINE, AND THAT IS DELIBERATE. There is no
form that both grants directories and binds a lifetime. Declaring every
combination is how an interface acquires four spawns and then eight, so the
combination is declared when something needs it. What a caller must not do
meanwhile is take `kal_process_spawn_with' and assume the binding.

Clause 8 is satisfied: five declarations added, none altered, no layout touched.
Clause 6.2 is satisfied by the property words --- an operation present and not
performable here is not a defect, because a caller can ask first, which is
exactly the argument clause 11 already records for the link operations.
…rations

⚠️⚠️ CI CAUGHT WHAT THE ADDITION ITSELF DID NOT: a specification may add a
capability position, and the suite that checks capability words carries its own
list of which positions are assigned. Adding the position and not the list makes
every implementation that claims it non-conforming:

    DID NOT HOLD [abi] the capability word contains no position the
                       specification has not assigned

Reported by openkal-macos and openkal-linux, on the release that assigned them.
⭐ The check is exactly right and the omission is mine --- it is the check that
exists so an implementation cannot extend the interface rather than implement it,
and it cannot tell an extension from a position the suite has not been told about.

`kal::fs::locks`, `kal::fs::capacity` and `kal::process::bound_lifetime` are added
to the modules, and to the three masks.

--- and the operations are OBSERVED, not merely declared --------------------

⚠️ Five declarations were added with no criterion between them. A specification
that adds an operation and no way to tell whether an implementation performs it
has added a name.

⭐⭐ THE LOCK OBSERVATION NEEDS NO SECOND PROGRAM, AND THAT IS THE WHOLE OF ITS
DESIGN. openkal states that the holder is the open FILE. One environment's oldest
form holds it by the PROCESS and releases every lock upon a node as soon as the
program closes any descriptor for it --- so a library that opened one file twice
destroyed its own lock. An implementation built on THAT form passes "a lock is
taken" and "a lock is released" and fails on exactly one observation:

    a second open file of the same name is refused, not granted

which this suite makes, in one program, with two `kal_file' handles.

`kal_fs_capacity': what is available is no more than what is held, and a caller
may ask for one of the two.

`kal_fs_set_modified_at': a DIRECTORY, which is the reason the declaration
exists --- and a file by the same operation, so that the new form is not a
directory-only path.

`kal_task_parallelism': the same answer each time, and one where contexts do not
run at once. ⭐ Zero means "cannot say" and the suite does not require a number,
because an implementation that will not say must be able to say so.

`kal_process_spawn_bound': ⚠️ where the position is NOT claimed, the operation
shall REFUSE rather than start something else. A caller that asked for a bound
lifetime asked for it; a program started without the binding is not the program
it asked to start, and quietly starting one is the failure the operation exists
to remove. openkal-macos and openkal-windows both take this path, so it is the
observation that governs two of the five implementations.
… have

`here()' is defined in the fs section. The process section has its own way to
name the working directory --- `kal::fs::working()' --- and the observation I
added called the other one, so the suite did not compile at all.

⚠️ AND IT COMPILED HERE, which is why it reached CI. A local build of this
package links against no implementation, so it stops at the link and never
reports what the compile of one section said. The build that catches this is the
one an implementation runs, and openkal-linux ran it on both compilers within
minutes of the push.
…orted

⚠️⚠️ THE SPECIFICATION HAS TWO SURFACES AND I ADDED TO ONE OF THEM.

`KAL_LOCK_SHARED', `KAL_LOCK_EXCLUSIVE' and `KAL_LOCK_WAIT' are macros in
`fs.h'. A consumer that writes `import openkal.fs' cannot see a macro --- so
those three positions were nameable by half the consumers of this specification
and not by the other half.

    error: use of undeclared identifier 'KAL_LOCK_EXCLUSIVE'

⭐ REPORTED BY THE SPECIFICATION'S OWN CONFORMANCE SUITE, WHICH IS ONE OF THE
CONSUMERS THAT CANNOT SEE MACROS. The suite imports the modules, so it is exactly
the reader that finds this --- and it found it on the release that added them.

They join `open_flags' in a kind of their own, for the reason that one has:
an intent and a capability word are different things, and a word that serves as
both can be passed to the wrong operation. `kal::fs::lock_range' goes with them,
as `kal::fs::open_file' goes with `open_flags'.

⚠️ The rule this leaves behind: a position added to the C header is not added
until it is in the module too. The property words got both because they were
added as a pair; these did not.
The entry names `kal_fs_set_modified_at' and `kal_fs_capacity' and justified
only the first. Found on a last read of the diff before merging: an entry that
names two additions and argues for one is how an addition arrives unargued.

Says why capacity belongs to `openkal.fs' and not `openkal.space' --- room on a
volume is a property of the names a caller can already reach --- what its absence
cost, which was a fixed number reported to a program checking for room, and why
it answers bytes rather than the block count and block size every environment
states in units of its own.
@Sunrisepeak
Sunrisepeak merged commit bfa5db4 into main Aug 30, 2026
12 checks passed
@Sunrisepeak
Sunrisepeak deleted the declarations-0.10 branch August 30, 2026 11:15
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.

A lock on a byte range: every environment can perform it, and there is no word for it

1 participant