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
32 changes: 32 additions & 0 deletions CONSTITUTION_CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,35 @@ The owner approves this exact assessment by reviewing and merging the contract
pull request. No provider response, schema publication, restricted raw store,
consumer implementation, downstream use, deployment, publication, or release
inherits approval.

## Studio #99 Comic Manifest v1 release-preparation candidate — 2026-10-07

Accountable owner: @andrewperis. Assessed source: Codex
`8044643bf888067f5a6e0d212f843d72e8787f2b` plus the exact release-preparation
candidate diff. Constitution v1.0.0, tag `constitution/v1.0.0`, Studio commit
`a9cc8a503aa30e17820edc62ac95f7cbe10e0564`. Profiles: universal;
repository/production-system; stable-contract release. Scope is the separate
Comic Manifest 1.0.0 catalog, its builder path and focused tests, and the
publication checklist. The Comic Manifest schema/specification/fixture bytes,
five-contract Prompt SDK catalog, Context Builder catalog, locked dependencies,
and their historical tags/assets are unchanged. Status: **Transition required**;
this is not a release approval or publication record.

The Issue 98 owner golden acceptance cited by Platform PR #41 is bounded to the
proposed catalog/golden/oracle bytes at Platform source head
`97dcd990e87bd0368ddcbecf66d1a6718bc7629e`, recorded in chat at
`2026-10-07T19:15:38Z`. PR #41 states that it was not a submitted GitHub review.
It does not approve this Codex release-preparation diff, the Codex release
assets, a consumer adoption, or final publication. No human review of those
release artifacts is claimed here.

The owner-provided Epic #6 A3 AI-review exception applies to code-merge review
only. It authorizes AI review within that merge scope; it does not authorize an
A4 release decision, tag, release asset, or publication. No release/publication
exception is provided. No human review of the final release artifacts is
claimed here, and a separate explicit owner release approval remains required.
The release gate remains open for exact-head review/merge, a fresh
locked-dependency audit, final license/NOTICE review, post-merge reproducible
builds, and owner approval of exact artifact tuples. No tag, release, asset,
npm publication, visibility change, production trust, or Epic #6 closeout is
authorized or represented.
6 changes: 6 additions & 0 deletions releases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ For the additive Context Builder family use the independent
[#86 checklist](context-builder-v1.md) and explicit `context-builder-v1` catalog.
The historical five-contract catalog below must not be republished at its existing tags.

For the additive Comic Manifest family use the independent
[#99 checklist](comic-manifest-v1.md) and explicit `comic-manifest-v1` catalog.
It contains only `comic-manifest` v1.0.0 at
`contract/comic-manifest/v1.0.0`; neither the Prompt SDK nor Context Builder
catalog or immutable bytes are republished by this catalog.

Owner: @andrewperis. This is issue #72 release preparation, not evidence that
the contracts are already published. Existing provisional notices remain true
until the steps below complete. No release or tag is created by the builder.
Expand Down
8 changes: 8 additions & 0 deletions releases/comic-manifest-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"version": "1.0.0",
"repository": "DefinitelySecureStudio/codex",
"constitution_commit": "a9cc8a503aa30e17820edc62ac95f7cbe10e0564",
"contracts": [
{ "name": "comic-manifest", "schema": "schemas/json/comic-manifest/v1/comic-manifest.schema.json" }
]
}
125 changes: 125 additions & 0 deletions releases/comic-manifest-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Comic Manifest v1 publication checklist — Studio #99

Owner: @andrewperis. This is release preparation only. It creates no tag,
release, asset, npm publication, visibility change, security-policy change, or
production permission. The candidate catalog contains only the additive
`comic-manifest` contract; the Prompt SDK five-contract and Context Builder
single-contract catalogs remain separate and their historical releases stay
unchanged.

## Candidate and review boundary

- Contract: `comic-manifest` 1.0.0, exact tag
`contract/comic-manifest/v1.0.0`, schema ID
`urn:definitely-secure:contract:comic-manifest:1.0.0:comic-manifest`.
- The contract combines production manifest, build-result, public-release, and
detached approval definitions in the one reviewed closed schema. It does not
authorize execution, publication, access, or Canon promotion.
- Normative sources are RFC 0007, RFC 0008, the Comic Manifest v1 specification,
versioning policy, schema README, and synthetic fixtures. The owner-approved
golden/catalog/oracle packet recorded by Platform #41 is evidence only for
those exact proposed files at source head
`97dcd990e87bd0368ddcbecf66d1a6718bc7629e` (chat acceptance
`2026-10-07T19:15:38Z`). It was not a GitHub review and is not release approval.
No approved golden, oracle, source, or scenario bytes are modified here.
- Constitution v1.0.0 is pinned to `constitution/v1.0.0`, Studio commit
`a9cc8a503aa30e17820edc62ac95f7cbe10e0564`. The owner's task-specific Epic
#6 A3 AI-review exception applies to code-merge review only. It does not
authorize an A4 release decision, tag, release asset, or publication; no
release/publication exception is provided. No human review of final release
artifacts is claimed here, and separate explicit owner release approval
remains required.
- Existing dependency versions and lock bytes are unchanged. Review the exact
lockfile audit report and the Apache-2.0 LICENSE, NOTICE, and any
THIRD_PARTY_NOTICES included in the bundle before approving release.
- A fresh `npm audit --json` against this Codex preparation branch's lockfile
reports zero advisories; rerun it against the exact merged head before
publication.

## Build and verify locally

1. Merge the reviewed Codex preparation change only after the contract owner
approves its exact head. Then use the clean merged Codex commit as the
release source; a branch, PR head, or local preparation build is not the
final release identity.
2. Run the full Codex test suite and the fresh locked-dependency and
license/NOTICE review. Resolve Blocker/Major findings before release.
3. On a clean checkout, build twice into two new external directories:

```sh
node scripts/build-contract-release.mjs /absolute/new/codex-comic-1 comic-manifest-v1
node scripts/build-contract-release.mjs /absolute/new/codex-comic-2 comic-manifest-v1
```

For a local repository snapshot whose verified remote source commit has a
different commit object but the exact same tree, both commands may append
`--source-commit VERIFIED_COMMIT --source-tree VERIFIED_TREE`. First make
the source commit object available locally (for example, fetch that exact
public commit without tags) and confirm the GitHub commit API's tree. The
builder independently resolves the supplied commit object's tree, requires
it to match both the supplied tree and the clean local `HEAD` tree, then
reads all bundled files from the verified source commit. Every builder Git invocation
sets `GIT_NO_REPLACE_OBJECTS=1` for object checks and source reads, without
changing global Git configuration. Regression tests create isolated replace
refs and prove they cannot change either accepted or rejected commit/tree
relationships. A commit-shaped caller string without a verified local
commit object fails closed. A normal merged checkout uses `HEAD` and needs
no override.

4. Compare every filename and byte across both directories. Each directory has
exactly these three expected outputs:
`comic-manifest-v1.0.0.schema.json`,
`comic-manifest-v1.0.0.bundle.json`, and
`comic-manifest-v1.0.0.manifest.json`. The manifest binds the exact source
commit, schema ID, tag, schema/bundle URIs, declared media types, sizes and
SHA-256 values. Its schema and bundle `assets` entries are the two payload
assets; the manifest file is attached as the third release asset and has its
own independently recorded digest in the approval packet. This avoids a
self-referential manifest digest.
5. Inspect the schema and bundle bytes, check every decoded bundle member's
relative path, byte count and digest, and confirm LICENSE and NOTICE are
included. The bundle carries the reviewed repository source for provenance;
only the single Comic Manifest contract is an asset/tag in this catalog.
6. Confirm the exact release/tag target, asset names and immutable-release
setting. `application/schema+json` and `application/json` describe the
expected downloaded file contents. GitHub upload/download response MIME
headers are transport metadata and may be `application/octet-stream`; record
both values separately and never rewrite the declared contract media type to
match the transport header.

## Owner-approved publication sequence

This checklist is not itself permission to publish. After review/merge, request
an explicit owner decision naming the exact merged Codex commit and all three
asset sizes/digests. Only then may the owner create a draft release at
`contract/comic-manifest/v1.0.0`, attach the schema, bundle, and manifest, and
review the notes. Download all three draft assets independently and compare
every byte with both clean builds. Publish once only after this review and
download check. Then independently verify that the release is published and
immutable, the exact tag resolves to the approved commit, and fresh downloads
still match all identities. Never move the tag or replace an asset; a correction
requires a new reviewed version.

## Consumer handoff and compatibility

After Codex publication, Platform separately downloads the exact assets, checks
the tag/commit, sizes and digests, regenerates its validator from the downloaded
schema, reviews the consumer lock, and passes its supported CI. Platform's
component tag proposal is `comic-manifest/v1.0.0`; its additive package release
candidate is 1.2.0. Neither candidate is final until that adoption and its own
owner release decision are complete. Prompt SDK v1, Context Package v1, and
Context Builder v1 immutable artifacts are not republished.

Compatibility remains exact kind/version negotiation. A compatible correction
requires a reviewed patch release; additive contract capabilities require an
explicitly supported version; breaking meaning requires an accepted RFC, new
major, affected-consumer inventory, side-by-side fixtures, migration and
rollback plan, deprecation release, and dated support window. No retirement or
support window starts in this initial release preparation.

Issue #101 / Build Orchestrator receives verified Codex and Platform artifact
tuples, conformance and verification evidence, API/CLI and adapter boundaries,
limits, known limitations, and unresolved production-trust/owner gates. This
handoff does not add scheduling, retries, providers, rendering, production
trust, access renewal, canon, or publishing automation. Close #99 and Epic #6
only after the actual approved publications and fresh-download verifications.
76 changes: 67 additions & 9 deletions scripts/build-contract-release.mjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,69 @@
import { execFileSync } from 'node:child_process';
import { mkdir, writeFile } from 'node:fs/promises';
import { lstat, mkdir, realpath, stat, writeFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import { resolve, relative, isAbsolute } from 'node:path';
import { resolve, relative, isAbsolute, sep, dirname, basename } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const root = fileURLToPath(new URL('../', import.meta.url));
const git = (...args) => execFileSync('git', args, { cwd: root, maxBuffer: 16 * 1024 * 1024 });
const gitAt = cwd => (...args) => execFileSync('git', args, {
cwd, env: { ...process.env, GIT_NO_REPLACE_OBJECTS: '1' }, maxBuffer: 16 * 1024 * 1024
});
const git = gitAt(root);
const sha = bytes => 'sha256:' + createHash('sha256').update(bytes).digest('hex');
export async function buildContractRelease(destination, catalogName = 'prompt-sdk-v1') {
if (!['prompt-sdk-v1', 'context-builder-v1'].includes(catalogName)) throw new Error('Unknown release catalog.');

export function verifySourceIdentity(sourceIdentity, localTree, checkoutRoot = root) {
const hasRemoteIdentity = sourceIdentity?.commit !== undefined || sourceIdentity?.tree !== undefined;
if (!hasRemoteIdentity) return false;
if (!/^[a-f0-9]{40}$/.test(sourceIdentity.commit ?? '') || !/^[a-f0-9]{40}$/.test(sourceIdentity.tree ?? '')) {
throw new Error('Supplied source identity must use full Git commit and tree IDs.');
}
const checkoutGit = gitAt(checkoutRoot);
let verifiedTree;
try {
if (checkoutGit('cat-file', '-t', sourceIdentity.commit).toString().trim() !== 'commit') throw new Error('not a commit object');
verifiedTree = checkoutGit('rev-parse', sourceIdentity.commit + '^{tree}').toString().trim();
} catch {
throw new Error('Supplied source commit is not present as a verified local Git commit object.');
}
if (verifiedTree !== sourceIdentity.tree || verifiedTree !== localTree) {
throw new Error('Supplied source commit must resolve to the exact matching local Git tree.');
}
return true;
}

export async function resolveExternalOutput(destination, checkoutRoot = root) {
if (!destination) throw new Error('Provide a new output directory outside the checkout.');
const output = resolve(destination), rel = relative(root, output);
if (!rel || (!rel.startsWith('..' + '/') && !isAbsolute(rel))) throw new Error('Output must be outside the checkout.');
const requested = resolve(destination), missing = [];
let ancestor = requested;
while (true) {
try { await lstat(ancestor); break; }
catch (error) {
if (error.code !== 'ENOENT') throw error;
const parent = dirname(ancestor);
if (parent === ancestor) throw error;
missing.unshift(basename(ancestor));
ancestor = parent;
}
}
if (!missing.length) throw new Error('Output directory must be new.');
const physicalAncestor = await realpath(ancestor);
if (!(await stat(physicalAncestor)).isDirectory()) throw new Error('Output path must descend from an existing directory.');
const output = resolve(physicalAncestor, ...missing);
const checkout = await realpath(checkoutRoot);
const rel = relative(checkout, output);
if (!rel || (!rel.startsWith('..' + sep) && rel !== '..' && !isAbsolute(rel))) {
throw new Error('Output must be outside the checkout.');
}
return output;
}

export async function buildContractRelease(destination, catalogName = 'prompt-sdk-v1', sourceIdentity = {}) {
if (!['prompt-sdk-v1', 'context-builder-v1', 'comic-manifest-v1'].includes(catalogName)) throw new Error('Unknown release catalog.');
const output = await resolveExternalOutput(destination);
if (git('status', '--porcelain').length) throw new Error('Commit or remove working-tree changes before building.');
const commit = git('rev-parse', 'HEAD').toString().trim();
const localCommit = git('rev-parse', 'HEAD').toString().trim();
const localTree = git('rev-parse', localCommit + '^{tree}').toString().trim();
const hasRemoteIdentity = verifySourceIdentity(sourceIdentity, localTree);
const commit = hasRemoteIdentity ? sourceIdentity.commit : localCommit;
const catalog = JSON.parse(git('show', commit + ':releases/' + catalogName + '.json'));
const files = git('ls-tree', '-r', '--name-only', '-z', commit).toString().split('\0').filter(Boolean).sort();
const entries = files.map(path => {
Expand Down Expand Up @@ -42,5 +93,12 @@ export async function buildContractRelease(destination, catalogName = 'prompt-sd
return releases;
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
console.log(JSON.stringify(await buildContractRelease(process.argv[2], process.argv[3]), null, 2));
const options = process.argv.slice(4);
const identity = {};
for (let index = 0; index < options.length; index += 2) {
const key = options[index], value = options[index + 1];
if (!['--source-commit', '--source-tree'].includes(key) || !value || value.startsWith('--')) throw new Error('Unknown or incomplete argument.');
identity[key === '--source-commit' ? 'commit' : 'tree'] = value;
}
console.log(JSON.stringify(await buildContractRelease(process.argv[2], process.argv[3], identity), null, 2));
}
Loading
Loading