diff --git a/spacecraft-steelbore-standard.skill b/spacecraft-steelbore-standard.skill index 30aa159..920f190 100644 Binary files a/spacecraft-steelbore-standard.skill and b/spacecraft-steelbore-standard.skill differ diff --git a/spacecraft-steelbore-standard.zip b/spacecraft-steelbore-standard.zip index 14e470b..d78fb01 100644 Binary files a/spacecraft-steelbore-standard.zip and b/spacecraft-steelbore-standard.zip differ diff --git a/spacecraft-steelbore-standard/SKILL.md b/spacecraft-steelbore-standard/SKILL.md index 71e1098..4c25ef0 100644 --- a/spacecraft-steelbore-standard/SKILL.md +++ b/spacecraft-steelbore-standard/SKILL.md @@ -8,7 +8,7 @@ description: > Spacecraft Software-umbrella project — even if the user doesn't explicitly mention the Standard. If the user mentions "Spacecraft Software", a Spacecraft Software subproject name, or asks you to work on anything in the Spacecraft Software ecosystem, consult this skill immediately. It encodes - The Steelbore Standard v2.03 (§19-§26 assurance + requirements + V&V; §13 design systems; §3.1.1 TypeScript; §5.7 AGENTS.md; §6.4 contribution targets; §5.6 skill packaging; §11 palettes + §11.6 system theme; §18 accessibility; §17 progress reporting; §3.3 security-by-design) so + The Steelbore Standard v2.04 (§19-§26 assurance + requirements + V&V; §13 design systems; §3.1.1 TypeScript; §5.7 AGENTS.md; §6.4 contribution targets; §5.6 skill packaging; §11 palettes + §11.6 system theme; §18 accessibility; §17 progress reporting; §3.3 security-by-design) so you never need to ask for it or have it attached to a prompt again. license: GPL-3.0-or-later maintainer: Mohamed Hammad @@ -17,7 +17,7 @@ website: https://Construct.SpacecraftSoftware.org/ # The Steelbore Standard — Compliance Reference -**Version:** 2.03 | **Date:** 2026-09-14 | **Author:** Mohamed Hammad +**Version:** 2.04 | **Date:** 2026-09-14 | **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/) @@ -1095,6 +1095,182 @@ SPDX headers (§4) cover license compliance mechanically; `CREDITS.md` is the human-readable narrative — who, what, and how the upstream work shaped the Spacecraft Software artifact. +### §15.4 — Authoring Model Attribution + +§15.2 records who maintains a Spacecraft Software artifact and §15.3 records whose +work it stands on. Neither records *what produced it*. When a language model drafts +a requirements document, an implementation plan, or a task list — or codes a project +outright — that fact stays legible for about as long as the session lasts. Which +model, at what reasoning effort, served by which provider, driven by which harness: +the answer lives in the maintainer's memory of the session and decays quickly. + +This section obligates a record. §15.5 specifies its form — an append-only log, +`.agent-log.jsonl`, written by the harness. + +**The record is local, and deliberately so.** §15.5 requires the log to be +git-ignored, so it never reaches a clone and makes no published claim about the +artifact. It serves the working copy that produced the work: what the maintainer can +ask, months later, about which model and configuration did what, how often tests +passed on the first attempt, and what the work cost. An artifact that must tell +*consumers* who stands behind it uses §15.2, which is unchanged. + +Commit trailers do not serve this purpose. A `Co-Authored-By` line is unstructured, +is lost to squash-merges and to the history rewrites §6.3 permits, and names a +product without the reasoning effort, the inference provider, the harness, or any +measurement of the work. The §15.5 log is machine-readable and carries all of them. +The two are independent, both MAY be used, and §15.4 does not displace the trailer +convention. + +**Triggers** (any one obligates a record): + +- A Product Requirements Document (PRD). +- An implementation plan. +- A TODO or task list that drives implementation. +- A project whose initial implementation was substantially produced by a model. + +**Not triggered by:** + +- Chat answers, scratch output, and anything that is not committed. +- A model that only reviewed, critiqued, or reformatted an artifact it did not author. + +**Provider and harness are independent, and both are recorded.** The **provider** is +the service that served the inference — `Anthropic`, `zAI`, `Ollama Cloud`, +`OpenRouter`, `OpenCode Zen`, or a local `Ollama`. The **harness** is the agent +client that drove it — `Claude Code`, `OpenCode`, `Codex`, `Orca`, `OpenClaude`, +`ZCode`, `Antigravity IDE`, `Antigravity CLI`. The same model reaches a repository +through many combinations of the two, and it is the combination, not the model alone, +that makes a result reproducible. + +**Reasoning effort is recorded separately** in the `reasoning_effort` field, not +folded into the model identifier. Where a model exposes a thinking or effort setting, +the bare identifier is incomplete — the same weights at a different level produce +materially different work. Where a model exposes no such setting, the field is omitted. + +**A record is not an authorship claim.** The copyright holder and maintainer remain +as §15.2 states, and the artifact's license is unchanged. §15.4 records a production +fact, not legal authorship: it MUST NOT appear in an `SPDX-FileCopyrightText` tag, in +a `# Maintainer:` line, or in `--version` output, and it never displaces the §15.2 +attribution block. + +### §15.5 — Agent Work Log (`.agent-log.jsonl`) + +The record §15.4 obligates is an append-only JSON Lines log at the repository root, +named `.agent-log.jsonl`. One line is appended per completed task. The file is +created on first append; its absence is not an error, and a repository to which no +trigger has applied carries none. + +**Append-only.** Each line is one complete JSON object, written once and never +rewritten, reordered, or deleted. The file is not a JSON array: there is no enclosing +bracket and no separating comma, so an append is a single write that cannot corrupt +what precedes it. A process that rewrites earlier lines has destroyed the only +property the format provides. + +**Git-ignored, and excluded from agent context.** A `.gitignore` entry for +`.agent-log.jsonl` is REQUIRED. The log is working-copy state, not a published +artifact, and committing it makes every branch that runs an agent session conflict on +the same file. It SHOULD also be excluded from the file context agents are given +(`.cursorignore` and the equivalent for other harnesses): it grows without bound, and +an agent that reads its own log wastes context on data it cannot act upon. + +This does not conflict with §5.7, which requires `AGENTS.md` and `CLAUDE.md` to be +tracked. Those are context files that carry project knowledge a fresh clone needs; +the log is a local measurement record that a fresh clone has no use for. + +**The harness writes it, not the model.** Token counts are not observable by a model +during its own session, and a model instructed to report them will supply plausible +invented numbers. The log is therefore generated by the harness — a hook, wrapper, or +client feature with access to the real session record. **A field the harness cannot +determine is omitted, never estimated.** An absent field is a truthful statement that +the value is unknown; a guessed one silently corrupts every later query. + +**Timestamps** are RFC 3339 UTC with a `Z` suffix, per §14.3, which already requires +UTC + `Z` for all `jsonl` machine output. + +**Schema.** Unknown fields are permitted — a harness MAY record more than this table +names — but the required fields MUST be present on every line. + +| Field | Type | Required | Example | +|-----------------------|-------------|----------|----------------------------| +| `task_id` | `string` | Yes | `"T-102"` | +| `timestamp` | `string` | Yes | `"2026-09-14T20:25:00Z"` | +| `model` | `string` | Yes | `"claude-opus-5"` | +| `provider` | `string` | Yes | `"Anthropic"` | +| `harness` | `string` | Yes | `"Claude Code"` | +| `files_touched` | `string[]` | Yes | `["src/adapters/db.rs"]` | +| `reasoning_effort` | `string` | No | `"high"` | +| `subagent_role` | `string` | No | `"implementer"` | +| `test_pass_first_try` | `boolean` | No | `true` | +| `iterations` | `integer` | No | `2` | +| `tokens_in` | `integer` | No | `12450` | +| `tokens_out` | `integer` | No | `890` | + +`files_touched` holds repository-relative paths, so a log stays meaningful when the +working copy moves. A file edited outside the repository root is recorded by absolute +path instead: a relative path that escapes the root resolves nowhere once the working +copy has moved, which is the failure the relative form exists to avoid. `task_id` is +whatever identifier the surrounding process uses — an issue key, a plan milestone, or +the harness session id where nothing better exists. + +**Example.** Two tasks, the second by a different model under a different harness: + +```jsonl +{"task_id":"T-102","timestamp":"2026-09-14T20:25:00Z","model":"claude-opus-5","provider":"Anthropic","harness":"Claude Code","reasoning_effort":"high","subagent_role":"implementer","files_touched":["src/adapters/db.rs"],"test_pass_first_try":true,"tokens_in":12450,"tokens_out":890} +{"task_id":"T-103","timestamp":"2026-09-14T21:10:00Z","model":"o3-mini","provider":"OpenAI","harness":"Codex","subagent_role":"auditor","files_touched":["tests/db_test.rs"],"test_pass_first_try":false,"iterations":2,"tokens_in":18200,"tokens_out":1450} +``` + +**Reference implementation.** A Claude Code `SessionEnd` hook, treating one session +as one task. It reads the real transcript rather than asking the model anything, and +omits `model` if the transcript never names one: + +```python +#!/usr/bin/env python3 +"""SessionEnd hook: append one .agent-log.jsonl line per session.""" +import datetime, json, os, sys + +ev = json.load(sys.stdin) # session_id, transcript_path, cwd +tin = tout = 0 +model = None +files = set() + +with open(ev["transcript_path"], encoding="utf-8") as fh: + for line in fh: + try: + rec = json.loads(line) + except ValueError: + continue # tolerate a torn final line + msg = rec.get("message") or {} + usage = msg.get("usage") or {} + tin += usage.get("input_tokens", 0) + tout += usage.get("output_tokens", 0) + model = msg.get("model") or model + for block in msg.get("content") or []: + if isinstance(block, dict) and block.get("type") == "tool_use": + path = (block.get("input") or {}).get("file_path") + if path: + rel = os.path.relpath(path, ev["cwd"]) + # A file outside the repository has no meaningful + # relative path; record it absolute rather than as + # a ../../.. chain that resolves nowhere. + files.add(path if rel.startswith("..") else rel) + +entry = { + "task_id": os.environ.get("AGENT_TASK_ID", ev["session_id"]), + "timestamp": datetime.datetime.now(datetime.timezone.utc) + .strftime("%Y-%m-%dT%H:%M:%SZ"), + "model": model, + "provider": "Anthropic", + "harness": "Claude Code", + "files_touched": sorted(files), + "tokens_in": tin, + "tokens_out": tout, +} + +with open(os.path.join(ev["cwd"], ".agent-log.jsonl"), "a", + encoding="utf-8") as fh: + fh.write(json.dumps({k: v for k, v in entry.items() + if v is not None}) + "\n") +``` + --- ## §17 — Development Progress Tracking & Reporting diff --git a/spacecraft-steelbore-standard/references/CHANGELOG.md b/spacecraft-steelbore-standard/references/CHANGELOG.md index 424c6dc..44972e0 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.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. + - **v2.03 (2026-09-14):** **§11 stops restating colour values — `steelbore.toml` becomes the single source, and §11.4's tie-break clause is inverted.** §11 has always named `steelbore.toml` "the canonical machine-readable source" and instructed that **values are read, never retyped** — then printed all 157 of them itself, across eleven tables spanning §11.0 through §11.5. The instruction and the document disagreed, and §11.4 settled it in the document's favour: *"Where this document and that file disagree, this document governs and the file is corrected."* That clause was the cost of keeping two copies — something had to break the tie, and a specification is the natural winner. **v2.03 stops keeping two copies.** Every hex and RGB triple is removed from §11; each table names the token and the role it plays, with the value obtained from the file. Where dropping a value column would have destroyed meaning the column was converted rather than deleted: §11.1.1's "Variant hex" is now "Variant token" and reads `Plasma Orange Lift` where it read a hex, with unlifted rows saying `(unchanged)` rather than repeating their base; the seven per-palette "Color" headers became "Token"; and a §8.6 table carrying canvases by hex was converted the same way. **The normative heart is §11.4's inversion**: the file now governs every value and §11 governs the contract around it — which roles exist, which are mandatory, what the contrast guarantee covers, which palettes are adoptable. The two can no longer disagree about a colour because only one of them states one, and changing a colour is a single edit rather than an edit plus a search for every restatement. **The cost is written into §11.4 rather than left for a reader to find: §11 can no longer be implemented from the published document alone.** A reader of the PDF, HTML, or Info output gets role names, contracts, and palette membership, but needs `steelbore.toml` for any colour — a real reduction in self-sufficiency, accepted in exchange for values having exactly one definition. **Contrast ratios stay**, deliberately: a measured ratio and a WCAG threshold are textually identical (`15.09:1` is a measurement, `4.5:1` is the AA rule), so removing one class would have removed the other and taken §11.0.2's matrix with it; 168 ratios are preserved. **Unchanged:** every role name, the contrast guarantee, the palette family and its membership, §11.5's non-adoptable fidelity palettes, and §11.6's resolution order. No project conforming at v2.02 becomes non-conformant at v2.03. This is the skill-side sync of Standard PR #36. This skill's own frontmatter `description` measures 994 characters under the §5.6 cap — only the version number moved. - **v2.02 (2026-09-12):** **§6.5's CI gate corrected — it reads the index, not the working tree.** v1.51 required CI to fail when "a tracked text file contains a CR byte" and left the implementation to the reader; the reference implementation grepped the working tree (`git grep -Il $'\r'`), and the accompanying note claimed `git grep` "honors `.gitattributes`, so a pinned exception is invisible to it and needs no exclusion list." **That claim is false, and it breaks the exception §6.5 itself grants.** A file pinned `*.bat text eol=crlf` is stored LF in the repository and *checked out CRLF by design* — that is the entire point of the pin. `git grep` reads the working tree, finds the CR the pin deliberately put there, and fails the build. The section granted an exception in one row and shipped a gate that punished it in the next, and the two rows only appeared to agree because no repository had yet needed a pinned exception. It was found the moment one did: `achernar` carries a `gradlew.bat`, which a Windows batch interpreter requires in CRLF. **The gate now reads what is stored.** `git ls-files --eol` reports each file's index line ending as `i/lf`, `i/crlf` or `i/mixed`, and any `i/crlf` or `i/mixed` is the violation — CRLF *in the repository*, which is what §6.5 was always about. A pinned `eol=crlf` file reports `i/lf` and passes, correctly; a path pinned `-text` is declared binary, is not a text file at all, and is skipped with its blob left exactly as it is — which is what a saved MIME archive or a binary git failed to auto-detect actually needs; and a file committed with CRLF before `.gitattributes` existed reports `i/crlf` and fails, which is exactly the case the section exists to catch. Validated against real repositories: `ironway` reports exactly 129 (matching an independent count), `bravais` 2, `achernar` 2, `standard` and `engram` 0. The row now specifies the mechanism rather than leaving it to the implementer, and states plainly that a working-tree grep is **not** a correct implementation. **A second fact the correction surfaced, worth recording because it changes what adoption costs:** `.gitattributes` does not retroactively normalize blobs already committed with CRLF. `bravais` has carried `* text=auto eol=lf` for months and still reports two `i/crlf` files; the attribute governs future writes and checkouts, and existing blobs stay as they are until `git add --renormalize` rewrites them. Adopting §6.5 in a repository that already carries CRLF is therefore two acts, not one — the config files, and a renormalization commit — and a rollout that ships only the first leaves the damage in place while reporting success. Nothing else in §6.5 changes. This is the skill-side sync of Standard PR #34. This skill's own frontmatter `description` measures 993 characters under the §5.6 cap — only the version number moved.