@opencanon/canon 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -14
- package/dist/cli.js +1 -1
- package/dist/commands/lock.js +39 -1
- package/dist/commands/lock.js.map +1 -1
- package/dist/commands/new.js +12 -2
- package/dist/commands/new.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +11 -4
- package/dist/core/validate.d.ts +4 -0
- package/dist/core/validate.js +23 -6
- package/dist/core/validate.js.map +1 -1
- package/dist/templates/conventions.js +4 -2
- package/dist/templates/conventions.js.map +1 -1
- package/dist/templates/metadata.d.ts +1 -1
- package/dist/templates/metadata.js +3 -2
- package/dist/templates/metadata.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,36 +1,171 @@
|
|
|
1
1
|
# @opencanon/canon
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Validation toolkit for shared fiction worldbuilding. CLI + library.
|
|
4
|
+
|
|
5
|
+
- **CLI binary**: `canon` — scaffold repos, run compliance checks, generate lock files
|
|
6
|
+
- **Library**: pure validation core + adapters (filesystem, GitHub)
|
|
7
|
+
- **Schema**: metadata v1.2, check report v2
|
|
8
|
+
|
|
9
|
+
---
|
|
4
10
|
|
|
5
11
|
## Install
|
|
6
12
|
|
|
7
13
|
```bash
|
|
8
|
-
npm install -g @opencanon/canon
|
|
14
|
+
npm install -g @opencanon/canon # CLI (global)
|
|
15
|
+
npm install @opencanon/canon # Library (local)
|
|
9
16
|
```
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
---
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
canon
|
|
18
|
-
canon
|
|
19
|
-
canon
|
|
20
|
+
## CLI Commands
|
|
21
|
+
|
|
22
|
+
| Command | Description | Exit code |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `canon init [dir]` | Scaffold repo: `canon/`, `stories/`, `.canonrc.json`, `CONVENTIONS.md` | 0 |
|
|
25
|
+
| `canon new story <id>` | Create `stories/<id>/metadata.json` from template | 0 or 1 |
|
|
26
|
+
| `canon new character <id>` | Create `canon/characters/<id>/definition.json` | 0 or 1 |
|
|
27
|
+
| `canon new location <id>` | Create `canon/worldbuilding/locations/<id>.json` | 0 or 1 |
|
|
28
|
+
| `canon check [dir]` | Run all compliance checks. **0** = all pass, **1** = any fail or no stories | 0 or 1 |
|
|
29
|
+
| `canon lock [dir]` | Run compliance pre-check, then regenerate `canon.lock.json`. Genesis lock skips pre-check. | 0 or 1 |
|
|
30
|
+
|
|
31
|
+
ID constraint: `/^[a-z0-9][a-z0-9_-]*$/` — lowercase alphanumeric, hyphens, underscores.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Compliance Checks
|
|
36
|
+
|
|
37
|
+
Seven checks run per story. Check IDs are stable (additive-only, never removed).
|
|
38
|
+
|
|
39
|
+
| Check ID | Rule |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `metadata_schema_valid` | Required fields present, correct types, `schema_version === "1.2"` |
|
|
42
|
+
| `characters_valid` | Every `characters[]` entry exists in `canon/characters/` |
|
|
43
|
+
| `locations_valid` | Every `locations[]` entry exists in `canon/worldbuilding/locations/` |
|
|
44
|
+
| `timeline_consistent` | `timeline` is strict ISO date `YYYY-MM-DD`, round-trip validated |
|
|
45
|
+
| `continuity_valid` | `temporal_context.prev_episode`, `next_episode`, `thematic_echoes` all resolve |
|
|
46
|
+
| `canon_version_match` | `canon_ref` matches `canon.lock.json` `canon_commit` |
|
|
47
|
+
| `contributor_valid` | `contributor` field is present and non-empty |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Library Exports
|
|
52
|
+
|
|
53
|
+
Three entry points. Core is pure (no I/O). Adapters convert external data into `RepoModel`.
|
|
54
|
+
|
|
55
|
+
### `@opencanon/canon` — core
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import {
|
|
59
|
+
validateRepo, // (model: RepoModel) => RepoCheckReport
|
|
60
|
+
validateStory, // single story validation
|
|
61
|
+
checkCharacters, checkLocations, checkTimeline,
|
|
62
|
+
checkContinuity, checkCanonVersion, checkContributor,
|
|
63
|
+
checkMetadataSchema,
|
|
64
|
+
type RepoModel, type RepoCheckReport, type StoryCheckReport,
|
|
65
|
+
type CheckResult, type CheckId,
|
|
66
|
+
type CanonLock, type StoryMetadata, type CanonConfig,
|
|
67
|
+
type GitHubRepoInput, type GitHubTreeEntry,
|
|
68
|
+
} from "@opencanon/canon"
|
|
20
69
|
```
|
|
21
70
|
|
|
22
|
-
|
|
71
|
+
`RepoCheckReport.schemaVersion` is always `"check.v2"`. Includes `summary: { score, totalChecks, passingChecks }`.
|
|
72
|
+
|
|
73
|
+
### `@opencanon/canon/adapters/fs` — filesystem
|
|
23
74
|
|
|
24
75
|
```ts
|
|
25
|
-
import { validateRepo } from "@opencanon/canon"
|
|
26
76
|
import { loadRepoFromFs } from "@opencanon/canon/adapters/fs"
|
|
77
|
+
const model: RepoModel = loadRepoFromFs("/path/to/repo")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Reads `canon/`, `stories/`, `canon.lock.json` from disk. Returns `RepoModel`.
|
|
81
|
+
|
|
82
|
+
### `@opencanon/canon/adapters/github` — GitHub (pure conversion)
|
|
83
|
+
|
|
84
|
+
```ts
|
|
27
85
|
import { buildRepoModel } from "@opencanon/canon/adapters/github"
|
|
86
|
+
const model: RepoModel = buildRepoModel({ tree, files })
|
|
28
87
|
```
|
|
29
88
|
|
|
89
|
+
Takes pre-fetched GitHub Trees API entries + file contents. No HTTP, no Octokit. Your app fetches, this function converts.
|
|
90
|
+
|
|
91
|
+
**`GitHubRepoInput`**:
|
|
92
|
+
- `tree`: `{ path: string, type: "blob" | "tree", sha: string }[]`
|
|
93
|
+
- `files`: `Map<string, string>` — path → content for `canon.lock.json`, `stories/*/metadata.json`
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Repo Structure
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
<repo-root>/
|
|
101
|
+
├── canon/
|
|
102
|
+
│ ├── characters/<id>/definition.json
|
|
103
|
+
│ └── worldbuilding/locations/<id>.json
|
|
104
|
+
├── stories/<slug>/
|
|
105
|
+
│ ├── metadata.json # required
|
|
106
|
+
│ └── chapter-*.md # content
|
|
107
|
+
├── canon.lock.json # generated by `canon lock`
|
|
108
|
+
├── .canonrc.json # generated by `canon init`
|
|
109
|
+
└── CONVENTIONS.md # generated by `canon init`
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Metadata Schema v1.2
|
|
115
|
+
|
|
116
|
+
```jsonc
|
|
117
|
+
{
|
|
118
|
+
"schema_version": "1.2", // fixed
|
|
119
|
+
"canon_ref": "<commit-sha>", // must match canon.lock.json
|
|
120
|
+
"id": "episode-slug", // unique story ID
|
|
121
|
+
"episode": 1, // number
|
|
122
|
+
"title": { "ko": "...", "en": "..." },
|
|
123
|
+
"timeline": "2025-01-15", // strict YYYY-MM-DD
|
|
124
|
+
"synopsis": { "ko": "...", "en": "..." },
|
|
125
|
+
"characters": ["alice", "bob"], // must exist in canon/characters/
|
|
126
|
+
"locations": ["market-square"], // must exist in canon/worldbuilding/locations/
|
|
127
|
+
"contributor": "github-username", // immutable after creation
|
|
128
|
+
"canon_status": "canonical", // "canonical" | "non-canonical"
|
|
129
|
+
// optional:
|
|
130
|
+
"themes": ["identity"],
|
|
131
|
+
"canon_events": ["first-contact"],
|
|
132
|
+
"word_count": { "ko": 3200, "en": 2800 },
|
|
133
|
+
"temporal_context": {
|
|
134
|
+
"prev_episode": "episode-slug-prev",
|
|
135
|
+
"next_episode": null,
|
|
136
|
+
"thematic_echoes": ["related-slug"]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Lock File
|
|
144
|
+
|
|
145
|
+
`canon.lock.json` is generated by `canon lock`. Do not edit manually.
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"schema_version": "canon.lock.v2",
|
|
150
|
+
"canon_commit": "<git-HEAD-sha>",
|
|
151
|
+
"worldbuilding_hash": "<sha256-hex>",
|
|
152
|
+
"hash_algo": "sha256",
|
|
153
|
+
"generated_at": "2025-01-15T00:00:00.000Z",
|
|
154
|
+
"contributors": ["github-username"]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Hash algorithm: for each file under `canon/` in lexicographic path order, feed `relativePath + \0 + fileBytes + \0` into SHA-256.
|
|
159
|
+
|
|
160
|
+
`canon lock` runs compliance pre-check before generating. If any story fails → lock refused (exit 1). First lock (no existing `canon.lock.json`) skips pre-check.
|
|
161
|
+
|
|
30
162
|
---
|
|
31
163
|
|
|
32
|
-
|
|
33
|
-
|
|
164
|
+
## Migration from v1.1
|
|
165
|
+
|
|
166
|
+
See [MIGRATION-v1.2.md](./MIGRATION-v1.2.md).
|
|
167
|
+
|
|
168
|
+
---
|
|
34
169
|
|
|
35
170
|
## License
|
|
36
171
|
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ const program = new Command();
|
|
|
8
8
|
program
|
|
9
9
|
.name("canon")
|
|
10
10
|
.description("Canon worldbuilding CLI — scaffold, validate, and manage shared fiction universes")
|
|
11
|
-
.version("0.
|
|
11
|
+
.version("0.2.0");
|
|
12
12
|
program.addCommand(checkCommand);
|
|
13
13
|
program.addCommand(lockCommand);
|
|
14
14
|
program.addCommand(initCommand);
|
package/dist/commands/lock.js
CHANGED
|
@@ -3,6 +3,8 @@ import { resolve, join, relative, sep } from "node:path";
|
|
|
3
3
|
import { readFileSync, readdirSync, writeFileSync, existsSync } from "node:fs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
|
+
import { loadRepoFromFs } from "../adapters/fs.js";
|
|
7
|
+
import { validateRepo } from "../core/validate.js";
|
|
6
8
|
function collectFiles(dir) {
|
|
7
9
|
const results = [];
|
|
8
10
|
if (!existsSync(dir))
|
|
@@ -29,6 +31,22 @@ export const lockCommand = new Command("lock")
|
|
|
29
31
|
console.error("Error: canon/ directory not found");
|
|
30
32
|
process.exit(1);
|
|
31
33
|
}
|
|
34
|
+
// Pre-check: all stories must pass compliance before lock is allowed.
|
|
35
|
+
// Skip if no canon.lock.json exists yet (genesis lock — nothing to check against).
|
|
36
|
+
const model = loadRepoFromFs(repoRoot);
|
|
37
|
+
const isGenesis = model.canonLock === null;
|
|
38
|
+
if (!isGenesis) {
|
|
39
|
+
const report = validateRepo(model);
|
|
40
|
+
if (report.totalStories > 0 && report.passingStories < report.totalStories) {
|
|
41
|
+
const failing = report.stories.filter(s => !s.allPass);
|
|
42
|
+
console.error("Error: compliance check failed — lock refused");
|
|
43
|
+
for (const s of failing) {
|
|
44
|
+
const fails = s.checks.filter(c => !c.pass).map(c => c.id).join(", ");
|
|
45
|
+
console.error(` ${s.storyId}: ${fails}`);
|
|
46
|
+
}
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
32
50
|
// git rev-parse HEAD — hard error on failure
|
|
33
51
|
let canonCommit;
|
|
34
52
|
try {
|
|
@@ -58,12 +76,32 @@ export const lockCommand = new Command("lock")
|
|
|
58
76
|
hash.update("\0");
|
|
59
77
|
}
|
|
60
78
|
const worldbuildingHash = hash.digest("hex");
|
|
79
|
+
// Extract unique contributors from story metadata (append-only)
|
|
80
|
+
const contributors = new Set();
|
|
81
|
+
// Preserve existing contributors from previous lock
|
|
82
|
+
const existingLockPath = join(repoRoot, "canon.lock.json");
|
|
83
|
+
if (existsSync(existingLockPath)) {
|
|
84
|
+
try {
|
|
85
|
+
const prev = JSON.parse(readFileSync(existingLockPath, "utf-8"));
|
|
86
|
+
if (Array.isArray(prev.contributors)) {
|
|
87
|
+
for (const c of prev.contributors)
|
|
88
|
+
contributors.add(c);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch { /* ignore malformed lock */ }
|
|
92
|
+
}
|
|
93
|
+
// Add contributors from current stories
|
|
94
|
+
for (const [, { meta }] of model.stories) {
|
|
95
|
+
if (meta.contributor)
|
|
96
|
+
contributors.add(meta.contributor);
|
|
97
|
+
}
|
|
61
98
|
const lock = {
|
|
62
|
-
schema_version: "canon.lock.
|
|
99
|
+
schema_version: "canon.lock.v2",
|
|
63
100
|
canon_commit: canonCommit,
|
|
64
101
|
worldbuilding_hash: worldbuildingHash,
|
|
65
102
|
hash_algo: "sha256",
|
|
66
103
|
generated_at: new Date().toISOString(),
|
|
104
|
+
contributors: [...contributors].sort(),
|
|
67
105
|
};
|
|
68
106
|
const lockPath = join(repoRoot, "canon.lock.json");
|
|
69
107
|
writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/new.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { resolve, join } from "node:path";
|
|
3
|
-
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { metadataTemplate } from "../templates/metadata.js";
|
|
5
5
|
import { characterTemplate } from "../templates/character.js";
|
|
6
6
|
import { locationTemplate } from "../templates/location.js";
|
|
@@ -31,8 +31,18 @@ export const newCommand = new Command("new")
|
|
|
31
31
|
console.error(`Error: stories/${slug} already exists`);
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
|
+
// Read contributor from .canonrc.json author field
|
|
35
|
+
let contributor = "";
|
|
36
|
+
const rcPath = join(root, ".canonrc.json");
|
|
37
|
+
if (existsSync(rcPath)) {
|
|
38
|
+
try {
|
|
39
|
+
const rc = JSON.parse(readFileSync(rcPath, "utf-8"));
|
|
40
|
+
contributor = rc.author || "";
|
|
41
|
+
}
|
|
42
|
+
catch { /* ignore malformed rc */ }
|
|
43
|
+
}
|
|
34
44
|
mkdirSync(storyDir, { recursive: true });
|
|
35
|
-
writeFileSync(join(storyDir, "metadata.json"), metadataTemplate(slug));
|
|
45
|
+
writeFileSync(join(storyDir, "metadata.json"), metadataTemplate(slug, contributor));
|
|
36
46
|
console.log(`Created stories/${slug}/metadata.json`);
|
|
37
47
|
break;
|
|
38
48
|
}
|
package/dist/commands/new.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type { CanonLock, StoryMetadata, CharacterDefinition, LocationDefinition, RepoModel, GitHubTreeEntry, GitHubRepoInput, CheckId, CheckResult, StoryCheckReport, RepoCheckReport, CanonConfig, } from "./types.js";
|
|
2
|
-
export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
|
|
2
|
+
export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkContributor, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
|
package/dist/core/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
|
|
1
|
+
export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkContributor, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAA"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
* No protocol internals — these are platform-facing types only.
|
|
4
4
|
*/
|
|
5
5
|
export interface CanonLock {
|
|
6
|
-
schema_version: "canon.lock.
|
|
6
|
+
schema_version: "canon.lock.v2";
|
|
7
7
|
canon_commit: string;
|
|
8
8
|
worldbuilding_hash: string;
|
|
9
9
|
hash_algo: "sha256";
|
|
10
10
|
generated_at: string;
|
|
11
|
+
contributors: string[];
|
|
11
12
|
}
|
|
12
13
|
export interface StoryMetadata {
|
|
13
|
-
schema_version: "1.
|
|
14
|
+
schema_version: "1.2";
|
|
14
15
|
canon_ref: string;
|
|
15
16
|
id: string;
|
|
16
17
|
episode: number;
|
|
@@ -25,6 +26,7 @@ export interface StoryMetadata {
|
|
|
25
26
|
};
|
|
26
27
|
characters: string[];
|
|
27
28
|
locations: string[];
|
|
29
|
+
contributor: string;
|
|
28
30
|
themes?: string[];
|
|
29
31
|
canon_events?: string[];
|
|
30
32
|
canon_status: "canonical" | "non-canonical";
|
|
@@ -79,7 +81,7 @@ export interface GitHubRepoInput {
|
|
|
79
81
|
tree: GitHubTreeEntry[];
|
|
80
82
|
files: Map<string, string>;
|
|
81
83
|
}
|
|
82
|
-
export type CheckId = "metadata_schema_valid" | "characters_valid" | "locations_valid" | "timeline_consistent" | "continuity_valid" | "canon_version_match";
|
|
84
|
+
export type CheckId = "metadata_schema_valid" | "characters_valid" | "locations_valid" | "timeline_consistent" | "continuity_valid" | "canon_version_match" | "contributor_valid";
|
|
83
85
|
export interface CheckResult {
|
|
84
86
|
id: CheckId;
|
|
85
87
|
pass: boolean;
|
|
@@ -91,7 +93,12 @@ export interface StoryCheckReport {
|
|
|
91
93
|
allPass: boolean;
|
|
92
94
|
}
|
|
93
95
|
export interface RepoCheckReport {
|
|
94
|
-
schemaVersion: "check.
|
|
96
|
+
schemaVersion: "check.v2";
|
|
97
|
+
summary: {
|
|
98
|
+
score: number;
|
|
99
|
+
totalChecks: number;
|
|
100
|
+
passingChecks: number;
|
|
101
|
+
};
|
|
95
102
|
stories: StoryCheckReport[];
|
|
96
103
|
totalStories: number;
|
|
97
104
|
passingStories: number;
|
package/dist/core/validate.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export declare function checkCanonVersion(meta: StoryMetadata, canonLock: CanonL
|
|
|
29
29
|
* Validate metadata.json schema conformance.
|
|
30
30
|
*/
|
|
31
31
|
export declare function checkMetadataSchema(meta: Record<string, unknown>): CheckResult;
|
|
32
|
+
/**
|
|
33
|
+
* Validate that contributor field is present and non-empty.
|
|
34
|
+
*/
|
|
35
|
+
export declare function checkContributor(meta: StoryMetadata): CheckResult;
|
|
32
36
|
/**
|
|
33
37
|
* Run all checks for a single story.
|
|
34
38
|
*/
|
package/dist/core/validate.js
CHANGED
|
@@ -66,13 +66,13 @@ export function checkCanonVersion(meta, canonLock) {
|
|
|
66
66
|
* Validate metadata.json schema conformance.
|
|
67
67
|
*/
|
|
68
68
|
export function checkMetadataSchema(meta) {
|
|
69
|
-
const required = ["schema_version", "canon_ref", "id", "episode", "title", "timeline", "synopsis", "characters", "locations", "canon_status"];
|
|
69
|
+
const required = ["schema_version", "canon_ref", "id", "episode", "title", "timeline", "synopsis", "characters", "locations", "contributor", "canon_status"];
|
|
70
70
|
const missing = required.filter(f => !(f in meta));
|
|
71
71
|
if (missing.length > 0) {
|
|
72
72
|
return check("metadata_schema_valid", false, `Missing fields: ${missing.join(", ")}`);
|
|
73
73
|
}
|
|
74
|
-
if (meta.schema_version !== "1.
|
|
75
|
-
return check("metadata_schema_valid", false, `Expected schema_version "1.
|
|
74
|
+
if (meta.schema_version !== "1.2") {
|
|
75
|
+
return check("metadata_schema_valid", false, `Expected schema_version "1.2", got "${meta.schema_version}"`);
|
|
76
76
|
}
|
|
77
77
|
if (typeof meta.episode !== "number") {
|
|
78
78
|
return check("metadata_schema_valid", false, `episode must be a number`);
|
|
@@ -89,6 +89,13 @@ export function checkMetadataSchema(meta) {
|
|
|
89
89
|
}
|
|
90
90
|
return check("metadata_schema_valid", true);
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate that contributor field is present and non-empty.
|
|
94
|
+
*/
|
|
95
|
+
export function checkContributor(meta) {
|
|
96
|
+
const valid = typeof meta.contributor === "string" && meta.contributor.trim().length > 0;
|
|
97
|
+
return check("contributor_valid", valid, valid ? undefined : "contributor must be a non-empty string");
|
|
98
|
+
}
|
|
92
99
|
/**
|
|
93
100
|
* Run all checks for a single story.
|
|
94
101
|
*/
|
|
@@ -100,6 +107,7 @@ export function validateStory(input) {
|
|
|
100
107
|
checkTimeline(input.meta),
|
|
101
108
|
checkContinuity(input.meta, input.knownEpisodes),
|
|
102
109
|
checkCanonVersion(input.meta, input.canonLock),
|
|
110
|
+
checkContributor(input.meta),
|
|
103
111
|
];
|
|
104
112
|
return {
|
|
105
113
|
storyId: input.meta.id,
|
|
@@ -123,11 +131,20 @@ export function validateRepo(model) {
|
|
|
123
131
|
canonLock: model.canonLock,
|
|
124
132
|
}));
|
|
125
133
|
}
|
|
134
|
+
const totalStories = stories.length;
|
|
135
|
+
const passingStories = stories.filter(s => s.allPass).length;
|
|
136
|
+
const totalChecks = stories.reduce((sum, s) => sum + s.checks.length, 0);
|
|
137
|
+
const passingChecks = stories.reduce((sum, s) => sum + s.checks.filter(c => c.pass).length, 0);
|
|
126
138
|
return {
|
|
127
|
-
schemaVersion: "check.
|
|
139
|
+
schemaVersion: "check.v2",
|
|
140
|
+
summary: {
|
|
141
|
+
score: totalStories > 0 ? passingStories / totalStories : 0,
|
|
142
|
+
totalChecks,
|
|
143
|
+
passingChecks,
|
|
144
|
+
},
|
|
128
145
|
stories,
|
|
129
|
-
totalStories
|
|
130
|
-
passingStories
|
|
146
|
+
totalStories,
|
|
147
|
+
passingStories,
|
|
131
148
|
};
|
|
132
149
|
}
|
|
133
150
|
//# sourceMappingURL=validate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,13 +19,13 @@ canon.lock.json # Generated — do not edit manually
|
|
|
19
19
|
CONVENTIONS.md # This file
|
|
20
20
|
\`\`\`
|
|
21
21
|
|
|
22
|
-
## Metadata Schema (v1.
|
|
22
|
+
## Metadata Schema (v1.2)
|
|
23
23
|
|
|
24
24
|
Every story directory must contain a \`metadata.json\` with these required fields:
|
|
25
25
|
|
|
26
26
|
| Field | Type | Description |
|
|
27
27
|
|------------------|----------|--------------------------------------|
|
|
28
|
-
| schema_version | "1.
|
|
28
|
+
| schema_version | "1.2" | Fixed schema version |
|
|
29
29
|
| canon_ref | string | Commit SHA from canon.lock.json |
|
|
30
30
|
| id | string | Unique story identifier (slug) |
|
|
31
31
|
| episode | number | Episode number |
|
|
@@ -34,6 +34,7 @@ Every story directory must contain a \`metadata.json\` with these required field
|
|
|
34
34
|
| synopsis | {ko, en} | Bilingual synopsis |
|
|
35
35
|
| characters | string[] | Character IDs used in this story |
|
|
36
36
|
| locations | string[] | Location IDs used in this story |
|
|
37
|
+
| contributor | string | GitHub username (immutable) |
|
|
37
38
|
| canon_status | string | "canonical" or "non-canonical" |
|
|
38
39
|
|
|
39
40
|
## Compliance Checks
|
|
@@ -45,6 +46,7 @@ Run \`canon check\` to validate:
|
|
|
45
46
|
- **timeline_consistent** — timeline is a valid ISO date
|
|
46
47
|
- **continuity_valid** — temporal_context references point to existing episodes
|
|
47
48
|
- **canon_version_match** — canon_ref matches canon.lock.json commit
|
|
49
|
+
- **contributor_valid** — contributor field is present and non-empty
|
|
48
50
|
`;
|
|
49
51
|
}
|
|
50
52
|
//# sourceMappingURL=conventions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/templates/conventions.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO
|
|
1
|
+
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/templates/conventions.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDR,CAAA;AACD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function metadataTemplate(id: string): string;
|
|
1
|
+
export declare function metadataTemplate(id: string, contributor?: string): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function metadataTemplate(id) {
|
|
1
|
+
export function metadataTemplate(id, contributor = "") {
|
|
2
2
|
return JSON.stringify({
|
|
3
|
-
schema_version: "1.
|
|
3
|
+
schema_version: "1.2",
|
|
4
4
|
canon_ref: "",
|
|
5
5
|
id,
|
|
6
6
|
episode: 0,
|
|
@@ -9,6 +9,7 @@ export function metadataTemplate(id) {
|
|
|
9
9
|
synopsis: { ko: "", en: "" },
|
|
10
10
|
characters: [],
|
|
11
11
|
locations: [],
|
|
12
|
+
contributor,
|
|
12
13
|
canon_status: "non-canonical",
|
|
13
14
|
}, null, 2) + "\n";
|
|
14
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/templates/metadata.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,EAAU;
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/templates/metadata.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,WAAW,GAAG,EAAE;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,EAAE;QACb,EAAE;QACF,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACzB,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC5B,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE;QACb,WAAW;QACX,YAAY,EAAE,eAAe;KAC9B,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AACpB,CAAC"}
|