Skip to content

Commit 461d0d3

Browse files
V48 (specification-implementation): Artifact hierarchy generics + patch + product
Add @bitcode/artifact-generics (Artifact identity, ArtifactInfo, ArtifactStorage contract and storage requirements), @bitcode/generic-artifacts-patch (PatchArtifact path+op base for AssetPack patchfiles), and AssetPackPatchArtifact product in @bitcode/asset-packs-synthesis. @bitcode/artifacts becomes the S3/Supabase ArtifactStorage backend with BC re-exports of primitives.
1 parent c92eb6b commit 461d0d3

33 files changed

Lines changed: 1066 additions & 167 deletions

BITCODE_SPEC_V48_NOTES.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,20 @@ Measured-patch is the sole AssetPack implementation used by synthesize-deposits,
16521652
synthesize-reads, and settle-reads. Deposit option `contents` projects from
16531653
MeasuredPatchAssetPack (path+op patch + provenant paths; never raw source).
16541654

1655+
## Artifact hierarchy: generics + patch + product (Garrett, 2026-07-13)
1656+
1657+
```
1658+
@bitcode/artifact-generics # Artifact + ArtifactStorage contract
1659+
→ @bitcode/generic-artifacts-patch # PatchArtifact (path+op patchfiles)
1660+
→ @bitcode/asset-packs-synthesis # AssetPackPatchArtifact (product)
1661+
@bitcode/artifacts # S3/Supabase ArtifactStorage backend (BC)
1662+
```
1663+
1664+
Primitive storage requirements live in artifact-generics. Patch base stores
1665+
AssetPack path+op envelopes without requiring raw source. Product binds
1666+
`assetPackId`. Existing `saveArtifact` / `putArtifactAtKey` callers keep
1667+
`@bitcode/artifacts`.
1668+
16551669
## AssetPack product pipelines: no lens; Simple settle-reads (Garrett, 2026-07-13)
16561670

16571671
Removed the deposit|read **lens** on a single SynthesizeAssetPacks factory.

FAMILIARIZATION.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,26 @@ Package paths: `packages/asset-pack-generics/`, `packages/generic-asset-packs/me
225225
| Base | `MeasuredPatchAssetPack` | + measurements, neediness, provenant paths |
226226
| Product | deposit options / pipeline outputs | project from MeasuredPatchAssetPack |
227227

228+
### 3.1.4 Artifacts (primitive → patch → product)
229+
230+
```
231+
@bitcode/artifact-generics Artifact + ArtifactStorage contract
232+
233+
@bitcode/generic-artifacts-patch PatchArtifact (path+op patchfiles)
234+
235+
@bitcode/asset-packs-synthesis AssetPackPatchArtifact (product)
236+
@bitcode/artifacts concrete S3/Supabase storage (BC)
237+
```
238+
239+
Package paths: `packages/artifact-generics/`, `packages/generic-artifacts/patch/`,
240+
`packages/artifacts/`, product in `packages/asset-packs/synthesis/`.
241+
242+
| Layer | Type | Role |
243+
| --- | --- | --- |
244+
| Primitive | `Artifact` / `ArtifactStorage` | identity + storage requirements + save/putAtKey |
245+
| Base | `PatchArtifact` | path+op patchfile envelope for AssetPack patches |
246+
| Product | `AssetPackPatchArtifact` | patch bound to `assetPackId` for synthesis |
247+
228248
### 3.2.0 VCS
229249

230250

internal-docs/BITCODE_SOURCE_LAYOUT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ packages/generic-<family>/ # README only (no package.json)
225225
| `generic-executors/` | (package root) | `@bitcode/generic-executors` (sequential, parallel, …) |
226226
| `generic-executions/` | (package root) | `@bitcode/generic-executions` (process-root Execution) |
227227
| `context-generics/` | (package root) | `@bitcode/context-generics` (BC only; no separate Context state) |
228+
| `artifact-generics/` | (package root) | `@bitcode/artifact-generics` (Artifact + storage contract) |
229+
| `generic-artifacts/` | `patch/` | `@bitcode/generic-artifacts-patch` (PatchArtifact base) |
230+
| `artifacts/` | (package root) | `@bitcode/artifacts` (S3/Supabase ArtifactStorage backend) |
228231
| `generic-doc-comment-plugins/` | `doc-developing/` | `@bitcode/doc-comment-developing` |
229232

230233
**Do not** put a root `package.json` on the family folder. Workspace globs are

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"packages/generic-mcps/*",
2121
"packages/asset-packs/*",
2222
"packages/generic-asset-packs/*",
23+
"packages/generic-artifacts/*",
2324
"packages/asset-packs-pipelines/*",
2425
"packages/generic-doc-comment-plugins/*",
2526
"packages/pipelines/*",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# @bitcode/artifact-generics
2+
3+
**Artifact** primitive contracts — the minimum required to identity a stored
4+
artifact and express storage requirements without binding a backend.
5+
6+
## Hierarchy
7+
8+
```
9+
@bitcode/artifact-generics # this package (Artifact primitive)
10+
11+
@bitcode/generic-artifacts-patch # PatchArtifact base (patchfile storage)
12+
13+
@bitcode/asset-packs-synthesis # AssetPackPatchArtifact (product)
14+
@bitcode/artifacts # concrete S3/Supabase storage (BC)
15+
```
16+
17+
## Primitive surface
18+
19+
| Concept | Types |
20+
| --- | --- |
21+
| Identity | `ArtifactId`, `ArtifactKind`, `ARTIFACT_SCHEMA_PREFIX` |
22+
| Result | `ArtifactInfo` (url, size, name, etag?) |
23+
| Storage | `ArtifactStorage` (`save`, `putAtKey`), `ArtifactBytes` |
24+
| Requirements | `ArtifactStorageRequirements` (backend, content types, key law) |
25+
26+
## Law
27+
28+
- Primitive artifacts never embed raw product domain (AssetPack, Execution).
29+
- Backends implement `ArtifactStorage`; bases/products project into it.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
preset: 'ts-jest/presets/js-with-ts',
3+
testEnvironment: 'node',
4+
testMatch: ['**/__tests__/**/*.test.ts'],
5+
moduleNameMapper: {
6+
'^@bitcode/artifact-generics$': '<rootDir>/src/index.ts',
7+
'^@bitcode/artifact-generics/(.*)$': '<rootDir>/src/$1',
8+
},
9+
globals: {
10+
'ts-jest': { tsconfig: '<rootDir>/../../tsconfig.json', diagnostics: false },
11+
},
12+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@bitcode/artifact-generics",
3+
"version": "0.1.0",
4+
"private": true,
5+
"description": "Artifact primitive: identity, storage requirements, ArtifactInfo, ArtifactStorage contract",
6+
"main": "src/index.ts",
7+
"types": "src/index.ts",
8+
"exports": {
9+
".": "./src/index.ts",
10+
"./types": "./src/types.ts",
11+
"./storage": "./src/storage.ts"
12+
},
13+
"scripts": {
14+
"typecheck": "tsc --noEmit",
15+
"test": "pnpm exec jest --config jest.config.cjs --passWithNoTests"
16+
},
17+
"devDependencies": {
18+
"@types/jest": "^29.5.12",
19+
"@types/node": "^25.0.0",
20+
"jest": "^29.7.0",
21+
"ts-jest": "^29.1.1",
22+
"typescript": "^5.0.0"
23+
}
24+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
assertArtifactId,
3+
createArtifactIdentity,
4+
ARTIFACT_SCHEMA_PREFIX,
5+
DEFAULT_ARTIFACT_STORAGE_REQUIREMENTS,
6+
type Artifact,
7+
} from '../index';
8+
9+
describe('artifact-generics primitives', () => {
10+
it('builds a minimal Artifact without storage', () => {
11+
const artifact: Artifact = {
12+
identity: createArtifactIdentity({
13+
artifactId: 'artifact-test-1',
14+
kind: 'blob',
15+
}),
16+
contentType: 'application/octet-stream',
17+
name: 'out.bin',
18+
storage: null,
19+
};
20+
21+
expect(artifact.identity.schema).toBe(`${ARTIFACT_SCHEMA_PREFIX}.blob`);
22+
expect(artifact.storage).toBeNull();
23+
expect(DEFAULT_ARTIFACT_STORAGE_REQUIREMENTS.contentOpaque).toBe(true);
24+
});
25+
26+
it('rejects empty artifact ids', () => {
27+
expect(() => assertArtifactId('')).toThrow(/non-empty/);
28+
});
29+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @bitcode/artifact-generics
3+
*
4+
* Artifact **primitive** contracts for Bitcode Protocol.
5+
*
6+
* Prefer:
7+
* @bitcode/artifact-generics # this package
8+
* @bitcode/generic-artifacts-patch # PatchArtifact base
9+
* @bitcode/asset-packs-synthesis # AssetPackPatchArtifact product
10+
* @bitcode/artifacts # concrete S3/Supabase storage
11+
*/
12+
13+
export type {
14+
ArtifactId,
15+
ArtifactKind,
16+
ArtifactIdentity,
17+
ArtifactInfo,
18+
ArtifactBytes,
19+
Artifact,
20+
} from './types';
21+
export {
22+
ARTIFACT_SCHEMA_PREFIX,
23+
isArtifactId,
24+
assertArtifactId,
25+
createArtifactIdentity,
26+
} from './types';
27+
28+
export type {
29+
ArtifactStorageRequirements,
30+
ArtifactStorage,
31+
SaveArtifact,
32+
PutArtifactAtKey,
33+
} from './storage';
34+
export {
35+
DEFAULT_ARTIFACT_STORAGE_REQUIREMENTS,
36+
DEFAULT_ARTIFACT_CONTENT_TYPE,
37+
} from './storage';
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Artifact storage contract + requirements (backend-agnostic).
3+
*
4+
* Concrete backends (S3, Supabase) implement `ArtifactStorage`.
5+
* Prefer `@bitcode/artifacts` for the default S3→Supabase implementation.
6+
*/
7+
8+
import type { ArtifactBytes, ArtifactInfo } from './types';
9+
10+
/** Declared requirements every Artifact storage backend must satisfy. */
11+
export interface ArtifactStorageRequirements {
12+
/**
13+
* At least one durable backend must be configured at runtime
14+
* (e.g. S3 bucket + region, or Supabase URL + key).
15+
*/
16+
requiresConfiguredBackend: true;
17+
/**
18+
* Keys must be unique; timestamp + UUID + name is the admitted generation law
19+
* when the caller does not supply an explicit key.
20+
*/
21+
keyGeneration: 'timestamp-uuid-name' | 'explicit-key';
22+
/** Content-type is always required (default application/octet-stream). */
23+
contentTypeRequired: true;
24+
/** Both binary and text payloads are admitted. */
25+
acceptsBinaryAndText: true;
26+
/**
27+
* Raw product source (AssetPack protected blobs) must never be stored under
28+
* artifact APIs without an explicit product policy — storage is content-opaque.
29+
*/
30+
contentOpaque: true;
31+
}
32+
33+
export const DEFAULT_ARTIFACT_STORAGE_REQUIREMENTS: ArtifactStorageRequirements = {
34+
requiresConfiguredBackend: true,
35+
keyGeneration: 'timestamp-uuid-name',
36+
contentTypeRequired: true,
37+
acceptsBinaryAndText: true,
38+
contentOpaque: true,
39+
};
40+
41+
export const DEFAULT_ARTIFACT_CONTENT_TYPE = 'application/octet-stream';
42+
43+
/**
44+
* Backend-agnostic storage port.
45+
* Implementations: `@bitcode/artifacts` (S3 primary, Supabase fallback).
46+
*/
47+
export interface ArtifactStorage {
48+
/**
49+
* Store bytes under an auto-generated key derived from `name`.
50+
* Returns public URL + size metadata.
51+
*/
52+
save(
53+
buffer: ArtifactBytes,
54+
name: string,
55+
contentType?: string,
56+
): Promise<ArtifactInfo>;
57+
58+
/**
59+
* Store bytes at an explicit key (stable paths, logs, upserts).
60+
*/
61+
putAtKey(
62+
key: string,
63+
buffer: ArtifactBytes,
64+
contentType?: string,
65+
): Promise<ArtifactInfo>;
66+
}
67+
68+
export type SaveArtifact = ArtifactStorage['save'];
69+
export type PutArtifactAtKey = ArtifactStorage['putAtKey'];

0 commit comments

Comments
 (0)