diff --git a/.github/check-token-names.py b/.github/check-token-names.py new file mode 100644 index 0000000..e4871d6 --- /dev/null +++ b/.github/check-token-names.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Mohamed Hammad +# SPDX-License-Identifier: GPL-3.0-or-later +"""Fail when a palette token name in steelbore.toml describes two colours. + +Palette token names look per-palette, but every consumer that resolves a name +to a value treats them as one flat namespace. `check-palette-css.py` maps +`--void-navy` to "void navy" and walks every `[palettes.*]` table +first-writer-wins; a name bound to two hexes therefore resolves to whichever +palette appears earlier in the file, and a stylesheet declaring that token is +checked against the wrong palette's value while reporting success. + +That is what happened to `Signal Green`, which named `#28C76F` in Steelbore +Blue and `#9ECE6A` in Tokyo Night from v1.39 until the v2.06 rename. It never +misfired only because no stylesheet happened to declare it. This gate makes the +next one a build failure instead of a latent one. + +Shared tones are the reason this checks values rather than banning reuse +outright: `Ember Red`, `Solar Amber`, `Mint Signal` and `Ember Lift` are +deliberately carried across palettes at one value each, and that is correct. +What is never correct is one name meaning two colours. +""" + +from __future__ import annotations + +import pathlib +import sys +import tomllib + +REPO = pathlib.Path(__file__).resolve().parent.parent +TOML = REPO / "steelbore-color-palette" / "assets" / "steelbore.toml" + + +def main() -> int: + try: + data = tomllib.loads(TOML.read_text(encoding="utf-8")) + except FileNotFoundError: + print(f"error: {TOML} not found", file=sys.stderr) + return 1 + except tomllib.TOMLDecodeError as exc: + print(f"error: {TOML} is not valid TOML: {exc}", file=sys.stderr) + return 1 + + palettes = data.get("palettes") + if not palettes: + print("error: steelbore.toml has no [palettes.*] tables", file=sys.stderr) + return 1 + + # name -> hex -> [palette slugs] + seen: dict[str, dict[str, list[str]]] = {} + for slug, palette in palettes.items(): + for key, value in palette.items(): + if key == "reference" or not isinstance(value, str): + continue + seen.setdefault(key.strip().lower(), {}).setdefault( + value.upper(), [] + ).append(slug) + + collisions = {name: v for name, v in seen.items() if len(v) > 1} + if not collisions: + print(f"token names OK ({len(seen)} distinct names across {len(palettes)} palettes)") + return 0 + + for name, by_hex in sorted(collisions.items()): + print(f"`{name}` names {len(by_hex)} different colours:", file=sys.stderr) + for hexval, slugs in sorted(by_hex.items()): + print(f" {hexval} in {', '.join(sorted(slugs))}", file=sys.stderr) + print( + " A token name is a flat namespace across the whole file — rename one,\n" + " or make them the same colour if they were meant to be a shared tone.", + file=sys.stderr, + ) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/generate-palette-css.py b/.github/generate-palette-css.py new file mode 100644 index 0000000..5e35db2 --- /dev/null +++ b/.github/generate-palette-css.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Mohamed Hammad +# SPDX-License-Identifier: GPL-3.0-or-later +"""Regenerate the generated prologue of spacecraft.css from steelbore.toml. + +`spacecraft.css` is two files in one. The prologue — the header comment, the +@font-face rules, and the `:root` custom properties — restates facts that live +in `steelbore.toml`: nine hexes, the palette's role bindings, and the Standard +version §11 was last amended at. The body below it is hand-authored layout that +references those properties through `var()` and has no business being generated. + +§11.4 says values are read, never retyped. Before this script the prologue +retyped them, and the `Palette: … v1.34` line in the header had been wrong +since v1.35. `check-palette-css.py` caught a value that drifted; nothing caught +the version pin, because nothing generated it. + +So: everything between the sentinels is derived, and `--check` fails the build +when the file no longer matches what the TOML implies. The body is left exactly +as found. + +Regenerate with: python3 .github/generate-palette-css.py +Verify with: python3 .github/generate-palette-css.py --check + +Both the canonical copy and its synced derivatives are written, so the three +stay byte-identical by construction rather than by discipline. +""" + +from __future__ import annotations + +import pathlib +import sys +import tomllib + +REPO = pathlib.Path(__file__).resolve().parent.parent +TOML = REPO / "steelbore-color-palette" / "assets" / "steelbore.toml" + +# The canonical copy first; the rest are synced derivatives (§11.4). Paths +# outside this repository are handled by their own repo's CI — the Standard's +# workflow already compares its copy against the canonical one. +TARGETS = [ + REPO / "steelbore-color-palette" / "assets" / "spacecraft.css", + REPO / "spacecraft-texinfo-document" / "assets" / "spacecraft.css", +] + +BEGIN = "/* >>> generated from steelbore.toml — do not edit below this line <<< */" +END = "/* >>> end generated — hand-authored layout follows <<< */" + +# Theme whose tokens the stylesheet exposes. Modern is the §11.4 default and +# the palette every document uses absent a declaration. +THEME = "steelbore" + +# Role tokens in the order they should appear, canvas and surfaces first. +ROLE_ORDER = [ + "background", + "surface", + "surface-alt", + "foreground", + "accent", + "structure", + "success", + "error", + "warning", + "focus", + "border", +] + + +def kebab(name: str) -> str: + """`Void Navy` -> `void-navy`, the inverse of check-palette-css.py's mapping.""" + return "-".join(name.strip().lower().split()) + + +def build_prologue(data: dict) -> str: + meta = data["meta"] + palette = data["palettes"][THEME] + theme = data["themes"][THEME] + + # Map hex -> palette token name, so the CSS property is named after the + # token rather than after the role (a hex may serve two roles). + by_hex = {v.upper(): k for k, v in palette.items() if k != "reference"} + + # Collect the distinct hexes this theme actually binds, in role order, and + # record every role each one carries so the comment is derived, not typed. + roles_for: dict[str, list[str]] = {} + order: list[str] = [] + for role in ROLE_ORDER: + hexval = theme[role].upper() + if hexval not in roles_for: + order.append(hexval) + roles_for[hexval] = [] + roles_for[hexval].append(role) + + rows = [] + for hexval in order: + token = by_hex.get(hexval) + if token is None: + raise SystemExit( + f"error: {hexval} is bound by [themes.{THEME}] but is not a named " + f"token in [palettes.{THEME}] — every role must resolve to a token" + ) + rows.append((f"--{kebab(token)}:", hexval, ", ".join(roles_for[hexval]), token)) + + prop_w = max(len(r[0]) for r in rows) + body = "\n".join( + f" {prop:<{prop_w}} {hexval}; /* {token} — {roles} */" + for prop, hexval, roles, token in rows + ) + + return f"""{BEGIN} + +/* Palette: {meta['standard']}. Typography: §12 (Share Tech Mono / Inconsolata, + both OFL). Values are generated from steelbore.toml — §11.4 requires they be + read, never retyped. Regenerate with .github/generate-palette-css.py. + + Fonts are resolved locally and never fetched (§9.1): an installed copy is + used where one exists, and the generic monospace fallback covers the rest. + No third-party subresource is requested at render time. */ + +@font-face {{ + font-family: 'Share Tech Mono'; + src: local('Share Tech Mono'), local('ShareTechMono-Regular'); + font-display: swap; +}} + +@font-face {{ + font-family: 'Inconsolata'; + src: local('Inconsolata'), local('Inconsolata-Regular'); + font-display: swap; +}} + +:root {{ +{body} + color-scheme: dark; +}} + +{END}""" + + +def render(path: pathlib.Path, prologue: str) -> str: + text = path.read_text(encoding="utf-8") + head, sep, rest = text.partition(BEGIN) + if not sep: + raise SystemExit( + f"error: {path} has no generated-section sentinel.\n" + f" Expected a line reading:\n {BEGIN}\n" + " Add the sentinels around the header/@font-face/:root block first." + ) + _, sep2, tail = rest.partition(END) + if not sep2: + raise SystemExit(f"error: {path} has an opening sentinel but no closing {END!r}") + return head + prologue + tail + + +def main() -> int: + check = "--check" in sys.argv[1:] + data = tomllib.loads(TOML.read_text(encoding="utf-8")) + prologue = build_prologue(data) + + stale = [] + for path in TARGETS: + if not path.exists(): + print(f"error: {path} not found", file=sys.stderr) + return 1 + want = render(path, prologue) + if check: + if path.read_text(encoding="utf-8") != want: + stale.append(path) + else: + path.write_text(want, encoding="utf-8") + print(f"wrote {path.relative_to(REPO)} from steelbore.toml") + + if check: + if stale: + for path in stale: + print( + f"`{path.relative_to(REPO)}` is stale — it no longer matches " + "`steelbore.toml`.", + file=sys.stderr, + ) + print( + "Run `python3 .github/generate-palette-css.py` and commit the result. " + "steelbore.toml is the single source (§11.4) — correct the TOML, " + "never the CSS.", + file=sys.stderr, + ) + return 1 + print(f"spacecraft.css is in sync with steelbore.toml ({len(TARGETS)} copies)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 378fad4..ca1c8f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,6 +151,25 @@ jobs: - name: steelbore.scm matches steelbore.toml run: python3 .github/generate-steelbore-scm.py --check + # Token names look per-palette but are one flat namespace to every + # consumer that resolves a name to a value — check-palette-css.py walks + # all [palettes.*] first-writer-wins, so a name bound to two hexes checks + # a stylesheet against the wrong palette and still reports success. + # `Signal Green` was exactly that from v1.39 until the v2.06 rename, and + # never misfired only because no stylesheet declared it. Shared tones + # (Ember Red, Solar Amber, Mint Signal, Ember Lift) are deliberate and + # pass — the gate gets stricter only about one name meaning two colours. + - name: Palette token names are unambiguous + run: python3 .github/check-token-names.py + + # spacecraft.css's header, @font-face rules and :root block are generated + # from steelbore.toml for the same reason steelbore.scm is: they restated + # nine hexes and a §11 version pin, and the pin had been wrong since v1.35 + # because nothing generated it. check-palette-css.py catches a drifted + # value; only this catches a drifted version or a reintroduced @import. + - name: spacecraft.css matches steelbore.toml + run: python3 .github/generate-palette-css.py --check + # construct-cli/ is the one real build surface in this repo — everything else # is markdown. It went ungated until now, which is how a clippy break # (items_after_test_module in src/sources/skillmd.rs) sat unnoticed on main: diff --git a/README.md b/README.md index 9387f5c..361e087 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ the rules re-attached to every prompt. | [`spacecraft-theme-factory`](spacecraft-theme-factory/) | Generates Spacecraft Software-compliant themes for IDEs and terminals. | | [`spacecraft-typescript-guidelines`](spacecraft-typescript-guidelines/) | Type-safe highly-concurrent TypeScript guidance (targeting TypeScript 7.0+) — Go native compiler optimizations, Project References (`composite`/`incremental`), strict type checking, non-blocking asynchronous event loops, CPU-parallel worker pools (`Piscina`), V8 engine tuning (hidden classes), and Zod data validation boundaries. | | [`spacecraft-zig-guidelines`](spacecraft-zig-guidelines/) | Memory-safe high-performance concurrent Zig guidance — `std.Thread.Pool` / `std.Io.Threaded`, atomics, allocator discipline, comptime safety, and CPU-bound scaling patterns. | -| [`steelbore-color-palette`](steelbore-color-palette/) | Single source of truth for the Steelbore palette family (Standard §11, last amended in v2.05) — ten palettes (Modern the default, plus Classic, Blue, BlackPinkPanther, MatrixGreen, NavyWhite, Tokyo Night, Hanzo Steel, and the two §11.5 fidelity palettes Solarized Dark/Light), 19 themes with per-background WCAG matrices, the §11.1 role-token contract, the §11.1.1 accessibility variants, the §11.6 system-theme resolution and declaration contract (`[resolution]`, polarity, light/dark pairing, `SPACECRAFT_THEME`, `/etc/steelbore/theme.toml`), and the shipped `assets/steelbore.toml` + `assets/spacecraft.css`. Consumer skills defer here for color values. | +| [`steelbore-color-palette`](steelbore-color-palette/) | Single source of truth for the Steelbore palette family (Standard §11, last amended in v2.06) — ten palettes (Modern the default, plus Classic, Blue, BlackPinkPanther, MatrixGreen, NavyWhite, Tokyo Night, Hanzo Steel, and the two §11.5 fidelity palettes Solarized Dark/Light), 19 themes with per-background WCAG matrices, the §11.1 role-token contract, the §11.1.1 accessibility variants, the §11.6 system-theme resolution and declaration contract (`[resolution]`, polarity, light/dark pairing, `SPACECRAFT_THEME`, `/etc/steelbore/theme.toml`), and the shipped `assets/steelbore.toml` + `assets/spacecraft.css`. Consumer skills defer here for color values. | ## Directory layout diff --git a/spacecraft-steelbore-standard/SKILL.md b/spacecraft-steelbore-standard/SKILL.md index d82134e..ef4f740 100644 --- a/spacecraft-steelbore-standard/SKILL.md +++ b/spacecraft-steelbore-standard/SKILL.md @@ -17,7 +17,7 @@ website: https://Construct.SpacecraftSoftware.org/ # The Steelbore Standard — Compliance Reference -**Version:** 2.05 | **Date:** 2026-09-15 | **Author:** Mohamed Hammad +**Version:** 2.06 | **Date:** 2026-09-15 | **Author:** Mohamed Hammad **Maintainer:** Mohamed Hammad | **Contact:** [Mohamed.Hammad@SpacecraftSoftware.org](mailto:Mohamed.Hammad@SpacecraftSoftware.org) **Copyright:** Copyright (C) 2026 Mohamed Hammad & Spacecraft Software | **License:** GPL-3.0-or-later **Website:** [https://Construct.SpacecraftSoftware.org/](https://Construct.SpacecraftSoftware.org/) @@ -728,6 +728,30 @@ Every Spacecraft Software application must satisfy **all three** PFA requirement When reviewing or designing any feature that touches data handling, permissions, or networking, verify all three PFA requirements are met. +### §9.1 — No Third-Party Subresources + +The three rows above are scoped to an **application**, and that left a gap: a +document is not an application, so nothing in §9 reached the HTML this standard +publishes — which loaded its §12 fonts from a third-party CDN, disclosing every +reader's IP, User-Agent and Referer on every page view. No tracker, no analytics +SDK: the letter of the first row was met while its purpose was not. + +**The rule is therefore a property of artifacts, not applications.** No +Spacecraft Software artifact — application, library, document, stylesheet, +diagram, slide, or generated page — fetches a subresource from a host the +project does not control at render time. + +| Rule | Detail | +|------|--------| +| Fonts resolve locally | Baseline is `@font-face` whose `src` names `local()` only, backed by the generic `monospace` fallback. An artifact needing faithful rendering for every reader MAY also ship the file beside itself, listed after `local()` — §12's licence whitelist exists so it may be redistributed. Bundling is a fidelity choice; the fetch is what is forbidden | +| All subresource classes | Scripts, stylesheets, images and media follow the same rule. A CDN reference is third-party whatever it carries | +| Degrading is not complying | A fallback that renders acceptably when the fetch fails does not cure the fetch. The request **is** the disclosure | +| Hyperlinks are unaffected | A link the reader chooses to follow is not a subresource; this governs what an artifact loads unasked | +| Exceptions are declared | Where an artifact genuinely cannot function without a third-party fetch, document it in `README.md` naming the host, the data disclosed, and why no bundled alternative exists — the same shape as a §3.1 exemption | + +A self-contained artifact is also offline-capable, reproducible, and immune to an +upstream host disappearing, so this costs little beyond the bytes it bundles. + --- ## §10 — Key Bindings @@ -1607,7 +1631,7 @@ Before finalising **any** Spacecraft Software artifact, mentally verify: - [ ] **§6.5** Text files are LF-terminated, UTF-8 without BOM, and end with a newline; `.gitattributes` (`* text=auto eol=lf`) and `.editorconfig` (`charset`, `end_of_line`, `insert_final_newline`) present at the repository root; CI fails on a CR byte in a tracked text file; CRLF exceptions (vendored upstream, `cmd.exe` scripts) pinned explicitly in `.gitattributes` - [ ] **§7** Shell scripts are POSIX-compatible; Nushell/Ion native variants provided where shell-native idioms are required; no Bashisms in shared scripts - [ ] **§8** Texinfo manual present for user-facing programs (`doc/.texi`); builds to `.info`, `.html`, and `.pdf`; `install-info` hook present in all three package manifests (§5.5) — N/A for scripts and internal tooling -- [ ] **§9** PFA: no tracking, minimal permissions, local storage default +- [ ] **§9** PFA: no tracking, minimal permissions, local storage default; **§9.1** no third-party subresources — fonts declared `local()`-first and bundled only if fidelity requires it, other assets shipped beside the artifact, nothing fetched from a host the project does not control, with any unavoidable exception declared in `README.md` - [ ] **§10** CUA + Vim-like key bindings planned/implemented; bindings user-remappable; assistive-technology modifier chords (NVDA/Orca/VoiceOver) not captured — N/A for projects registered as games (§18.5) - [ ] **§11** A registered palette is used — Steelbore Modern by default, or exactly one declared alternate (§11.4), never a mix; that palette's canvas is used unaltered; surface tokens are fills only, never text (§11.0.1); token-on-token pairings outside the palette's verified matrix measured before use; new apps expose colors via a named `Steelbore` theme binding the §11.1 role tokens — no bare hex literals in UI logic — and ship the palette's `-high-contrast` sibling - [ ] **§11.6** Theme resolution implemented in two stages — base palette (in-app selection, then `SPACECRAFT_THEME`, then the §11.6.4 system declaration, then the platform color scheme, then the project's §11.4 default), then variant overlay (a pinned variant, then `NO_COLOR` ⇒ `steelbore-mono`, then §18.1 accessible mode, then platform high contrast); the registered set covers §11.6.1's fifteen eleven-role themes; an unknown or unregistered slug falls through rather than failing; palette switches are atomic and whole-surface and carry the new canvas; resolved theme and deciding source reported under `--verbose`; no dependence on per-role environment variables — Steelbore OS additionally renders `/etc/steelbore/theme.toml`, exports `SPACECRAFT_THEME`, and keeps the platform color-scheme preference in agreement with the declared polarity (§11.6.5) — N/A for artifacts with no user-facing output diff --git a/spacecraft-steelbore-standard/references/CHANGELOG.md b/spacecraft-steelbore-standard/references/CHANGELOG.md index 6d9f304..a8401be 100644 --- a/spacecraft-steelbore-standard/references/CHANGELOG.md +++ b/spacecraft-steelbore-standard/references/CHANGELOG.md @@ -12,6 +12,8 @@ activation. The canonical record is the published Standard's own `standard/CHANGELOG.md` (extracted from §1 of the document itself in v1.37); this file mirrors it and must be synced to the same version and date. +- **v2.06 (2026-09-15):** **§9.1 closes a privacy gap this standard's own published HTML was falling through, and §11.3.1's `success` token is renamed to end a name collision.** §9's three PFA requirements are scoped to an *application*, and a stylesheet shipped with a document is not one — so the HTML at `Standard.SpacecraftSoftware.org` loaded its two §12 fonts from a third-party CDN, disclosing every reader's IP address, User-Agent and Referer on every page view with no notice and no opt-in. **No tracker was involved and no analytics SDK was shipped**, so the letter of the "No Tracking/No Ads" row was satisfied while its purpose plainly was not. That is a scoping defect, not a licence for the behaviour. §9.1 restates the rule as a property of **artifacts** rather than applications: nothing we ship fetches a subresource from a host the project does not control at render time. **The baseline is `local()`, not bundling** — an `@font-face` rule naming `local()` only, backed by the generic `monospace` fallback, uses an installed copy where one exists and degrades to a system font where none does, without ever opening a connection; an artifact needing faithful rendering for every reader may additionally ship the file beside itself, which §12's licence whitelist exists to permit. Bundling is a fidelity choice; the fetch is what is forbidden. Three readings are closed explicitly, each being a way the rule could have been made hollow: a graceful fallback does not cure the fetch, because the request *is* the disclosure; a hyperlink the reader chooses to follow is not a subresource; and an unavoidable dependency must be declared in `README.md` naming the host, the data disclosed, and why no bundled alternative exists. **The rename second.** `Signal Green` named two different colours — `#28C76F` in Steelbore Blue and `#9ECE6A` in Tokyo Night. Token names are one flat namespace to every consumer that resolves a name to a value: `check-palette-css.py` walks all `[palettes.*]` first-writer-wins, so a doubled name checks a stylesheet against the wrong palette's value and still reports success. It was latent only because no stylesheet declared it. **Blue's moves, to `Aurora Green`** — Tokyo Night is registered verbatim under §11.3.5 and its names evoke upstream, while Blue's are uniformly orbital, so the new name fits the set it joins. The hex does not change, so no ratio moves. **On the skill side** this release also ships three things the Standard describes but does not itself contain: a `check-token-names.py` CI gate failing when any token name maps to more than one hex; `spacecraft.css` rebuilt so its header, `@font-face` rules and `:root` block are **generated** from `steelbore.toml` rather than retyped (the `Palette: … v1.34` pin in that header had been wrong since v1.35 because nothing generated it); and the Google Fonts `@import` removed, verified by building the Standard's HTML and confirming zero `googleapis` references in the output. **Unchanged:** §9's three original rows, every palette value, every contrast ratio, the §11.1 role contract, and the palette family and its membership. No project conforming at v2.05 becomes non-conformant at v2.06 on the palette side; the §9.1 obligation is new. This is the skill-side sync of Standard PR #40. + - **v2.05 (2026-09-15):** **§11.3.6 registers a tenth palette — Steelbore Hanzo Steel, the family's first pure-black canvas.** The palette's colours are drawn from the poster art for *Kill Bill Vol. 1*, and it is anchored on **Sumi Black** and **Hanzo Gold**. **The name is the standard's own, deliberately.** §11.3.2 set a precedent for film-derived palette names, but a palette name is published prose in a CC-BY-SA document, and a film title is a trademark this project has no licence to use as a product label. Colours are not protectable and the inspiration is stated plainly in the section; the identifier is not borrowed. **The substantive decision is the red, and §11.3.6 explains it in the text rather than leaving a reader to compare hexes and wonder.** The source art's Venetian red measures **4.03:1** on Sumi Black and **3.39:1** on Scabbard Slate — under the 4.5:1 text floor on the canvas, and far enough under it on the surface that error prose would not have been readable at normal size. It ships deepened, as **Crimson Edge**, for exactly the reason §11.3.4 deepens NavyWhite's status hues: a conforming alternate is not a §11.5 fidelity palette, so nothing obliges it to reproduce a source value that misses the floor. The alternative was considered and rejected — shipping the verbatim red would have bought fidelity to a poster with an `error` token no application could set in body text, and would have made this the only conforming palette whose error role is restricted on all three of its own backgrounds. **The result is a palette with no restricted pairings at all**: every foreground token clears 4.5:1 against canvas, surface, and surface-alt, the weakest being `error` on `surface` at 4.85:1, and `steelbore-hanzosteel-high-contrast` lifts `error` alone to Ember Lift (9.58:1) because every other token already clears 7:1. **A second thing the section states rather than assumes:** three golds carry three distinct roles — `accent`, `warning`, `structure` — separated by luminance rather than hue, which is legible but is precisely the case §18.2.1 exists for, so the text records that every coloured status in this palette carries its `[WARN]` or `[ERROR]` tag. **Counts move throughout**, and they are the bulk of the diff: ten palettes and nineteen themes, eight conforming, six alternates in §11.3, and the §11.6.1 registered set grows from thirteen to **fifteen** — so the arithmetic in that subsection now reads *three of the fifteen were already required, so this adds twelve*. The §16 checklist's §11.6 bullet follows. **Unchanged:** every existing palette, every existing ratio, the §11.1 eleven-role contract, §11.4's one-palette rule and its inverted tie-break, §11.5's two non-adoptable fidelity palettes, and §11.6's two-stage resolution order. `steelbore-navywhite` remains the only light canvas, so §11.6.2's light/dark pairing is untouched and Hanzo Steel pairs to it like every other dark member. No project conforming at v2.04 becomes non-conformant at v2.05. This is the skill-side sync of Standard PR for v2.05; the values themselves land in `steelbore-color-palette`'s `assets/steelbore.toml`, which remains the single source (§11.4). This skill's own frontmatter `description` is unchanged — §11's palette list lives in `references/palettes.md`, not in the description. - **v2.04 (2026-09-14):** **§15.4 and §15.5 added — what produced an artifact becomes a machine-readable local record.** §15 has carried two attribution axes since the standard's early versions: §15.2 records who maintains an artifact, §15.3 records whose work it stands on. Neither records *what produced it*. When a model drafts a PRD, an implementation plan, or a task list — or codes a project outright — which model, at what reasoning effort, served by which provider, driven by which harness is knowledge that lives in the maintainer's memory of the session and decays within days. **§15.4 obligates a record; §15.5 specifies its form.** The form is an append-only JSON Lines log, **`.agent-log.jsonl`** at the repository root, one line per completed task. A draft of this section existed at v1.50 and was never landed; it required a *committed* table (an `AUTHORS.md`, or a closing `Authoring Model` section) written once per artifact. **That mechanism is not what ships.** The log is per-task rather than per-artifact, structured rather than prose, and **git-ignored rather than committed** — and the section says plainly what that costs: the record never reaches a clone and makes **no published claim** about the artifact. It serves the working copy that produced the work, answering what the maintainer can ask months later about which configuration did what, how often tests passed first try, and what the work cost. An artifact that must tell *consumers* who stands behind it still uses §15.2, unchanged. The `Co-Authored-By:` trailer is compared and kept: §15.4 faults it for being unstructured, for being lost to squash-merges and the history rewrites §6.3 permits, and for naming a product without the reasoning effort, provider, harness, or any measurement — **not** for recording activity, which is precisely what the log does too. The two are independent and both may be used. **Schema:** six required fields (`task_id`, `timestamp`, `model`, `provider`, `harness`, `files_touched`) and six optional (`reasoning_effort`, `subagent_role`, `test_pass_first_try`, `iterations`, `tokens_in`, `tokens_out`); unknown fields are permitted. `provider` and `harness` are required because §15.4's rationale turns on the combination of the three being what makes a result reproducible — the model alone does not. `reasoning_effort` is its own field rather than being folded into the model identifier, JSON having real fields where a table cell did not. Timestamps are RFC 3339 UTC + `Z`, which §14.3 already required of all `jsonl` output. **The harness writes the log, not the model**, and the section explains why: token counts are not observable by a model during its own session, and a model told to report them supplies plausible invented numbers — so **a field the harness cannot determine is omitted, never estimated**, an absent field being a truthful statement that the value is unknown. A reference `SessionEnd` hook is given that reads the real transcript; it records in-repo paths relative and out-of-repo paths absolute, since a relative path escaping the root resolves nowhere once the working copy moves. §15.5 also states the `.gitignore` entry is required and that the log SHOULD be excluded from agent file context, and notes explicitly that none of this conflicts with §5.7's requirement that `AGENTS.md` and `CLAUDE.md` be *tracked* — those carry project knowledge a fresh clone needs, the log is local measurement a fresh clone has no use for. **Unchanged:** §15.1–§15.3 in full, the §15.2 attribution block, the existing trailer convention, and §4.3 licensing metadata — a §15.4 record is a production fact, not legal authorship, and MUST NOT appear in an `SPDX-FileCopyrightText` tag, a `# Maintainer:` line, or `--version` output. The §15 chapter menu gains two entries and §16 gains one checklist bullet, marked N/A for hand-written artifacts. No project conforming at v2.03 becomes non-conformant at v2.04. diff --git a/spacecraft-texinfo-document/assets/spacecraft.css b/spacecraft-texinfo-document/assets/spacecraft.css index 6251cd7..83863cf 100644 --- a/spacecraft-texinfo-document/assets/spacecraft.css +++ b/spacecraft-texinfo-document/assets/spacecraft.css @@ -3,27 +3,47 @@ Spacecraft Software HTML theme for texi2any output. Apply with: texi2any --html --no-split --css-include=spacecraft.css FILE.texi - Palette: Steelbore Standard §11 (Steelbore 2, v1.34). Typography: §12 - (Share Tech Mono / Inconsolata, both OFL). Canonical copy: /spacecraft-software/construct/steelbore-color-palette/assets/spacecraft.css — the copies in standard/ and spacecraft-texinfo-document/assets/ are synced - derivatives; edit the canonical copy first and keep all three byte-identical. - Fonts load from Google Fonts; system monospace is the offline fallback. */ + derivatives, written by the same generator; keep all three byte-identical. */ -@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&family=Share+Tech+Mono&display=swap'); +/* >>> generated from steelbore.toml — do not edit below this line <<< */ + +/* Palette: The Steelbore Standard §11 (v2.06). Typography: §12 (Share Tech Mono / Inconsolata, + both OFL). Values are generated from steelbore.toml — §11.4 requires they be + read, never retyped. Regenerate with .github/generate-palette-css.py. + + Fonts are resolved locally and never fetched (§9.1): an installed copy is + used where one exists, and the generic monospace fallback covers the rest. + No third-party subresource is requested at render time. */ + +@font-face { + font-family: 'Share Tech Mono'; + src: local('Share Tech Mono'), local('ShareTechMono-Regular'); + font-display: swap; +} + +@font-face { + font-family: 'Inconsolata'; + src: local('Inconsolata'), local('Inconsolata-Regular'); + font-display: swap; +} :root { - --void-navy: #000027; /* canvas — background, all surfaces */ - --quantum-blue: #0E2A47; /* surface — elevated panels / cards */ - --deep-matrix: #0B1A12; /* surface — code / terminal wells */ - --platinum-mist: #D9DEE5; /* body text / default readout */ - --plasma-orange: #FF5E00; /* H1 / primary accent / visited link */ - --pulse-violet: #8A6CFF; /* H3 / structure / links / borders */ - --acid-lime: #B4FF00; /* H2 / success / focus indicator */ - --mars-red: #FF3B3B; /* error status */ - --plasma-magenta: #E445FF; /* warning / attention */ + --void-navy: #000027; /* Void Navy — background */ + --quantum-blue: #0E2A47; /* Quantum Blue — surface */ + --deep-matrix: #0B1A12; /* Deep Matrix — surface-alt */ + --platinum-mist: #D9DEE5; /* Platinum Mist — foreground */ + --plasma-orange: #FF5E00; /* Plasma Orange — accent */ + --pulse-violet: #8A6CFF; /* Pulse Violet — structure, border */ + --acid-lime: #B4FF00; /* Acid Lime — success, focus */ + --mars-red: #FF3B3B; /* Mars Red — error */ + --plasma-magenta: #E445FF; /* Plasma Magenta — warning */ + color-scheme: dark; } +/* >>> end generated — hand-authored layout follows <<< */ + body { background-color: var(--void-navy); color: var(--platinum-mist); diff --git a/spacecraft-theme-factory/SKILL.md b/spacecraft-theme-factory/SKILL.md index 6c8ad63..9d4d153 100644 --- a/spacecraft-theme-factory/SKILL.md +++ b/spacecraft-theme-factory/SKILL.md @@ -20,7 +20,7 @@ website: https://Construct.SpacecraftSoftware.org/ > is the **Steelbore 2** generation; the five v1.33 foreground tokens and the old > lifts are Classic's, not Modern's (§11.2) — Void Navy carries forward. > -> **§11 is a palette family (v2.05).** Steelbore Modern is the default and is +> **§11 is a palette family (v2.06).** Steelbore Modern is the default and is > what you emit unless the requester names another. Seven more are registered: > `steelbore-classic`, `steelbore-blue`, `steelbore-blackpinkpanther`, > `steelbore-matrixgreen`, `steelbore-navywhite`, `tokyonight`, and diff --git a/steelbore-color-palette/SKILL.md b/steelbore-color-palette/SKILL.md index 25bf5af..9ba2126 100644 --- a/steelbore-color-palette/SKILL.md +++ b/steelbore-color-palette/SKILL.md @@ -1,7 +1,7 @@ --- name: steelbore-color-palette description: > - Single source of truth for the Steelbore palette family (§11, last amended v2.05) + Single source of truth for the Steelbore palette family (§11, last amended v2.06) — ten palettes, their hex tokens, WCAG contrast matrices, the §11.1 role-token contract, every §11.1.1 accessibility variant, and the §11.6 system-theme contract. Modern is the default; Classic, Blue, BlackPinkPanther, @@ -25,7 +25,7 @@ website: https://Construct.SpacecraftSoftware.org/ **Copyright:** (C) 2026 Mohamed Hammad & Spacecraft Software | **License:** GPL-3.0-or-later **Website:** [https://Construct.SpacecraftSoftware.org/](https://Construct.SpacecraftSoftware.org/) -> **Authority chain:** The Steelbore Standard **§11** — last amended in v2.05 — +> **Authority chain:** The Steelbore Standard **§11** — last amended in v2.06 — > is the normative text; this skill is its canonical machine-readable mirror and > the **only** place palette hexes should be read from. The version cited is the > one in which §11 last *changed*, not the current document version: a release diff --git a/steelbore-color-palette/assets/spacecraft.css b/steelbore-color-palette/assets/spacecraft.css index 6251cd7..83863cf 100644 --- a/steelbore-color-palette/assets/spacecraft.css +++ b/steelbore-color-palette/assets/spacecraft.css @@ -3,27 +3,47 @@ Spacecraft Software HTML theme for texi2any output. Apply with: texi2any --html --no-split --css-include=spacecraft.css FILE.texi - Palette: Steelbore Standard §11 (Steelbore 2, v1.34). Typography: §12 - (Share Tech Mono / Inconsolata, both OFL). Canonical copy: /spacecraft-software/construct/steelbore-color-palette/assets/spacecraft.css — the copies in standard/ and spacecraft-texinfo-document/assets/ are synced - derivatives; edit the canonical copy first and keep all three byte-identical. - Fonts load from Google Fonts; system monospace is the offline fallback. */ + derivatives, written by the same generator; keep all three byte-identical. */ -@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&family=Share+Tech+Mono&display=swap'); +/* >>> generated from steelbore.toml — do not edit below this line <<< */ + +/* Palette: The Steelbore Standard §11 (v2.06). Typography: §12 (Share Tech Mono / Inconsolata, + both OFL). Values are generated from steelbore.toml — §11.4 requires they be + read, never retyped. Regenerate with .github/generate-palette-css.py. + + Fonts are resolved locally and never fetched (§9.1): an installed copy is + used where one exists, and the generic monospace fallback covers the rest. + No third-party subresource is requested at render time. */ + +@font-face { + font-family: 'Share Tech Mono'; + src: local('Share Tech Mono'), local('ShareTechMono-Regular'); + font-display: swap; +} + +@font-face { + font-family: 'Inconsolata'; + src: local('Inconsolata'), local('Inconsolata-Regular'); + font-display: swap; +} :root { - --void-navy: #000027; /* canvas — background, all surfaces */ - --quantum-blue: #0E2A47; /* surface — elevated panels / cards */ - --deep-matrix: #0B1A12; /* surface — code / terminal wells */ - --platinum-mist: #D9DEE5; /* body text / default readout */ - --plasma-orange: #FF5E00; /* H1 / primary accent / visited link */ - --pulse-violet: #8A6CFF; /* H3 / structure / links / borders */ - --acid-lime: #B4FF00; /* H2 / success / focus indicator */ - --mars-red: #FF3B3B; /* error status */ - --plasma-magenta: #E445FF; /* warning / attention */ + --void-navy: #000027; /* Void Navy — background */ + --quantum-blue: #0E2A47; /* Quantum Blue — surface */ + --deep-matrix: #0B1A12; /* Deep Matrix — surface-alt */ + --platinum-mist: #D9DEE5; /* Platinum Mist — foreground */ + --plasma-orange: #FF5E00; /* Plasma Orange — accent */ + --pulse-violet: #8A6CFF; /* Pulse Violet — structure, border */ + --acid-lime: #B4FF00; /* Acid Lime — success, focus */ + --mars-red: #FF3B3B; /* Mars Red — error */ + --plasma-magenta: #E445FF; /* Plasma Magenta — warning */ + color-scheme: dark; } +/* >>> end generated — hand-authored layout follows <<< */ + body { background-color: var(--void-navy); color: var(--platinum-mist); diff --git a/steelbore-color-palette/assets/steelbore.scm b/steelbore-color-palette/assets/steelbore.scm index 5980f8a..7dc5b85 100644 --- a/steelbore-color-palette/assets/steelbore.scm +++ b/steelbore-color-palette/assets/steelbore.scm @@ -14,7 +14,7 @@ ; read, never retyped. Editing this file instead of the TOML defeats the ; entire point: there would be two sources, and they would drift. ; -; Palette contract version 3.3.0 — The Steelbore Standard §11 (v2.05). +; Palette contract version 3.4.0 — The Steelbore Standard §11 (v2.06). (define-module (steelbore) #:export (steelbore-meta @@ -26,9 +26,9 @@ ;;; Metadata (§11 [meta]). (define steelbore-meta - `((version . "3.3.0") + `((version . "3.4.0") (date . "2026-09-15T00:00:00Z") - (standard . "The Steelbore Standard §11 (v2.05)") + (standard . "The Steelbore Standard §11 (v2.06)") (default-theme . "steelbore") (default-dark-theme . "steelbore") (default-light-theme . "steelbore-navywhite") diff --git a/steelbore-color-palette/assets/steelbore.toml b/steelbore-color-palette/assets/steelbore.toml index 1671b35..bcecf76 100644 --- a/steelbore-color-palette/assets/steelbore.toml +++ b/steelbore-color-palette/assets/steelbore.toml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # # Steelbore palette family — canonical color contract (Standard §11, last -# amended v2.05). Ten palettes and 19 themes: steelbore (Modern, DEFAULT), +# amended v2.06). Ten palettes and 19 themes: steelbore (Modern, DEFAULT), # steelbore-classic, six alternates (blue, blackpinkpanther, matrixgreen, # navywhite, tokyonight, hanzosteel), and the two §11.5 fidelity palettes # (solarized-dark, solarized-light) which are registered but non-conforming. @@ -26,12 +26,12 @@ fidelity-palettes = ["solarized-dark", "solarized-light"] # §11.5 — register # excluded: it binds the legacy six-role contract (§11.2), defines no surface # class, and carries an `info` token that is not one of §11.1's eleven roles. registered-set = ["steelbore", "steelbore-high-contrast", "steelbore-blue", "steelbore-blue-high-contrast", "steelbore-blackpinkpanther", "steelbore-blackpinkpanther-high-contrast", "steelbore-matrixgreen", "steelbore-matrixgreen-high-contrast", "steelbore-navywhite", "steelbore-navywhite-high-contrast", "tokyonight", "tokyonight-high-contrast", "steelbore-hanzosteel", "steelbore-hanzosteel-high-contrast", "steelbore-mono"] -version = "3.3.0" +version = "3.4.0" date = "2026-09-15T00:00:00Z" maintainer = "Mohamed Hammad " website = "https://Construct.SpacecraftSoftware.org/" license = "GPL-3.0-or-later" -standard = "The Steelbore Standard §11 (v2.05)" +standard = "The Steelbore Standard §11 (v2.06)" # ------------------------------------------------------------------------- # §11.6 — System theme resolution. Slugs only; this table never carries a @@ -179,7 +179,7 @@ reference = "blue-color-palette" "Dawn Sky" = "#3390FF" "Azure Hue" = "#66A3FF" "Azure Bright" = "#79B4FF" -"Signal Green" = "#28C76F" +"Aurora Green" = "#28C76F" "Ember Red" = "#FF6B6B" "Solar Amber" = "#FFC857" "Warm Thruster" = "#FF8A4B"