@sublang/slc 0.1.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.
Files changed (137) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/dist/app.d.ts +85 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +233 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/artifacts.d.ts +61 -0
  8. package/dist/artifacts.d.ts.map +1 -0
  9. package/dist/artifacts.js +97 -0
  10. package/dist/artifacts.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +26 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/cligent-agent.d.ts +11 -0
  16. package/dist/cligent-agent.d.ts.map +1 -0
  17. package/dist/cligent-agent.js +66 -0
  18. package/dist/cligent-agent.js.map +1 -0
  19. package/dist/compiled-executor.d.ts +42 -0
  20. package/dist/compiled-executor.d.ts.map +1 -0
  21. package/dist/compiled-executor.js +433 -0
  22. package/dist/compiled-executor.js.map +1 -0
  23. package/dist/config-file.d.ts +52 -0
  24. package/dist/config-file.d.ts.map +1 -0
  25. package/dist/config-file.js +159 -0
  26. package/dist/config-file.js.map +1 -0
  27. package/dist/config.d.ts +82 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +161 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/entry-module.d.ts +22 -0
  32. package/dist/entry-module.d.ts.map +1 -0
  33. package/dist/entry-module.js +216 -0
  34. package/dist/entry-module.js.map +1 -0
  35. package/dist/execution.d.ts +71 -0
  36. package/dist/execution.d.ts.map +1 -0
  37. package/dist/execution.js +207 -0
  38. package/dist/execution.js.map +1 -0
  39. package/dist/file-capability.d.ts +46 -0
  40. package/dist/file-capability.d.ts.map +1 -0
  41. package/dist/file-capability.js +234 -0
  42. package/dist/file-capability.js.map +1 -0
  43. package/dist/file-grants.d.ts +79 -0
  44. package/dist/file-grants.d.ts.map +1 -0
  45. package/dist/file-grants.js +133 -0
  46. package/dist/file-grants.js.map +1 -0
  47. package/dist/hash.d.ts +14 -0
  48. package/dist/hash.d.ts.map +1 -0
  49. package/dist/hash.js +34 -0
  50. package/dist/hash.js.map +1 -0
  51. package/dist/index.d.ts +20 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +22 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/interpreter.d.ts +48 -0
  56. package/dist/interpreter.d.ts.map +1 -0
  57. package/dist/interpreter.js +118 -0
  58. package/dist/interpreter.js.map +1 -0
  59. package/dist/invocation.d.ts +64 -0
  60. package/dist/invocation.d.ts.map +1 -0
  61. package/dist/invocation.js +148 -0
  62. package/dist/invocation.js.map +1 -0
  63. package/dist/link.d.ts +63 -0
  64. package/dist/link.d.ts.map +1 -0
  65. package/dist/link.js +172 -0
  66. package/dist/link.js.map +1 -0
  67. package/dist/markdown.d.ts +12 -0
  68. package/dist/markdown.d.ts.map +1 -0
  69. package/dist/markdown.js +63 -0
  70. package/dist/markdown.js.map +1 -0
  71. package/dist/normalize.md +68 -0
  72. package/dist/phase-runner.d.ts +64 -0
  73. package/dist/phase-runner.d.ts.map +1 -0
  74. package/dist/phase-runner.js +322 -0
  75. package/dist/phase-runner.js.map +1 -0
  76. package/dist/phase.d.ts +62 -0
  77. package/dist/phase.d.ts.map +1 -0
  78. package/dist/phase.js +111 -0
  79. package/dist/phase.js.map +1 -0
  80. package/dist/pin-closure.d.ts +19 -0
  81. package/dist/pin-closure.d.ts.map +1 -0
  82. package/dist/pin-closure.js +110 -0
  83. package/dist/pin-closure.js.map +1 -0
  84. package/dist/pin-currency.d.ts +55 -0
  85. package/dist/pin-currency.d.ts.map +1 -0
  86. package/dist/pin-currency.js +435 -0
  87. package/dist/pin-currency.js.map +1 -0
  88. package/dist/pin-generate.d.ts +46 -0
  89. package/dist/pin-generate.d.ts.map +1 -0
  90. package/dist/pin-generate.js +131 -0
  91. package/dist/pin-generate.js.map +1 -0
  92. package/dist/pin-paths.d.ts +11 -0
  93. package/dist/pin-paths.d.ts.map +1 -0
  94. package/dist/pin-paths.js +108 -0
  95. package/dist/pin-paths.js.map +1 -0
  96. package/dist/pins.d.ts +84 -0
  97. package/dist/pins.d.ts.map +1 -0
  98. package/dist/pins.js +265 -0
  99. package/dist/pins.js.map +1 -0
  100. package/dist/pipeline.d.ts +61 -0
  101. package/dist/pipeline.d.ts.map +1 -0
  102. package/dist/pipeline.js +147 -0
  103. package/dist/pipeline.js.map +1 -0
  104. package/dist/playbook-contract.d.ts +194 -0
  105. package/dist/playbook-contract.d.ts.map +1 -0
  106. package/dist/playbook-contract.js +180 -0
  107. package/dist/playbook-contract.js.map +1 -0
  108. package/dist/playbook-ports.d.ts +53 -0
  109. package/dist/playbook-ports.d.ts.map +1 -0
  110. package/dist/playbook-ports.js +217 -0
  111. package/dist/playbook-ports.js.map +1 -0
  112. package/dist/resolver.d.ts +68 -0
  113. package/dist/resolver.d.ts.map +1 -0
  114. package/dist/resolver.js +133 -0
  115. package/dist/resolver.js.map +1 -0
  116. package/dist/runner.d.ts +45 -0
  117. package/dist/runner.d.ts.map +1 -0
  118. package/dist/runner.js +506 -0
  119. package/dist/runner.js.map +1 -0
  120. package/dist/runtime-package.d.ts +7 -0
  121. package/dist/runtime-package.d.ts.map +1 -0
  122. package/dist/runtime-package.js +132 -0
  123. package/dist/runtime-package.js.map +1 -0
  124. package/dist/slc.config.template.yaml +5 -0
  125. package/dist/verify-coverage.d.ts +99 -0
  126. package/dist/verify-coverage.d.ts.map +1 -0
  127. package/dist/verify-coverage.js +1949 -0
  128. package/dist/verify-coverage.js.map +1 -0
  129. package/dist/verify-support.d.ts +10 -0
  130. package/dist/verify-support.d.ts.map +1 -0
  131. package/dist/verify-support.js +52 -0
  132. package/dist/verify-support.js.map +1 -0
  133. package/dist/verify.d.ts +393 -0
  134. package/dist/verify.d.ts.map +1 -0
  135. package/dist/verify.js +1790 -0
  136. package/dist/verify.js.map +1 -0
  137. package/package.json +76 -0
@@ -0,0 +1,322 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * SLC phase-runner facade for compiled `playbook` artifacts (PHEXEC-23,
5
+ * PHEXEC-24; DR-005).
6
+ *
7
+ * A compiled `playbook` artifact default-exports a `PlaybookRuntimeFactory`
8
+ * (`createPlaybookRuntime`). `slc` drives it host-side — `init` with the
9
+ * DR-010 session/legacy compatibility view over a `PlaybookPorts` adapter, one
10
+ * `handleBossInput` turn seeded from the {@link PhaseInput}, then `dispose` —
11
+ * and derives a {@link PhaseResult}
12
+ * (`ok`/`blocked`/`error`) that {@link mapPhaseResult} maps onto the DR-003
13
+ * protocol: `ok` proceeds to the generic checks, `blocked` is the BLOCKED
14
+ * outcome, and `error` stops the pipeline like a failed generic check.
15
+ *
16
+ * The non-interactive driving lives in the compiled executor; this module owns
17
+ * the shared facade types, the static `playbook`-format recognition the
18
+ * pin-currency validator uses, the seeding of a phase request into the
19
+ * runtime's single Boss turn (PHEXEC-29), and the host workspace contract a
20
+ * transformation-performing direct Captain call carries (PHEXEC-34). See
21
+ * specs/dev/phase-execution.md.
22
+ */
23
+ import ts from 'typescript';
24
+ /**
25
+ * Maps a compiled phase's {@link PhaseResult} onto the DR-003 execution-boundary
26
+ * outcome consumed by `runPhase` (PHEXEC-24).
27
+ *
28
+ * The facade result (owned by the DR-005 artifact contract) and the executor
29
+ * result (owned by the DR-003 boundary) are distinct types, so the compiled
30
+ * executor crosses the boundary through this seam rather than casting: `ok`
31
+ * proceeds to the generic checks, `blocked` is the BLOCKED outcome, and `error`
32
+ * stops the pipeline like a failed generic check, with diagnostics surfaced for
33
+ * every status.
34
+ */
35
+ export function mapPhaseResult(result) {
36
+ return { status: result.status, diagnostics: result.diagnostics };
37
+ }
38
+ const PLAYBOOK_FACTORY = 'createPlaybookRuntime';
39
+ /**
40
+ * Reports whether a compiled artifact's source resolves to the linked `playbook`
41
+ * format: a module exposing a `createPlaybookRuntime` default export (DR-005).
42
+ * This is the static byte-level recognition the pin-currency validator uses; the
43
+ * loader confirms the contract at run time.
44
+ */
45
+ export function resolvesToPlaybook(source) {
46
+ const file = ts.createSourceFile('artifact.playbook.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
47
+ const diagnostics = file.parseDiagnostics;
48
+ if (diagnostics?.some((item) => item.category === ts.DiagnosticCategory.Error)) {
49
+ return false;
50
+ }
51
+ if (!usesErasableTypeScript(file) || defaultExportCount(file) !== 1) {
52
+ return false;
53
+ }
54
+ if (factoryValueBindingCount(file, PLAYBOOK_FACTORY) !== 1)
55
+ return false;
56
+ const hasFactoryBinding = factoryBindingIsCallable(file, PLAYBOOK_FACTORY);
57
+ for (const statement of file.statements) {
58
+ if (ts.isFunctionDeclaration(statement) &&
59
+ statement.name?.text === PLAYBOOK_FACTORY &&
60
+ statement.body !== undefined &&
61
+ hasModifier(statement, ts.SyntaxKind.ExportKeyword) &&
62
+ hasModifier(statement, ts.SyntaxKind.DefaultKeyword) &&
63
+ isSynchronousFunction(statement)) {
64
+ return true;
65
+ }
66
+ if (ts.isExportAssignment(statement) &&
67
+ !statement.isExportEquals &&
68
+ ts.isIdentifier(unwrapExpression(statement.expression)) &&
69
+ (unwrapExpression(statement.expression).text ===
70
+ PLAYBOOK_FACTORY ||
71
+ // `const factory: PlaybookRuntimeFactory<…> = createPlaybookRuntime;
72
+ // export default factory;` — a typed alias of the factory is the
73
+ // factory; linkers use it to state contract conformance in erasable
74
+ // TypeScript.
75
+ aliasesFactory(file, unwrapExpression(statement.expression).text)) &&
76
+ hasFactoryBinding) {
77
+ return true;
78
+ }
79
+ if (ts.isExportDeclaration(statement) &&
80
+ statement.exportClause !== undefined) {
81
+ if (ts.isNamedExports(statement.exportClause) &&
82
+ statement.exportClause.elements.some((element) => !statement.isTypeOnly &&
83
+ !element.isTypeOnly &&
84
+ element.name.text === 'default' &&
85
+ (element.propertyName?.text ?? element.name.text) ===
86
+ PLAYBOOK_FACTORY) &&
87
+ statement.moduleSpecifier === undefined &&
88
+ hasFactoryBinding) {
89
+ return true;
90
+ }
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ /** True when `name` is a top-level `const <name> = createPlaybookRuntime;`. */
96
+ function aliasesFactory(file, name) {
97
+ for (const statement of file.statements) {
98
+ if (!ts.isVariableStatement(statement))
99
+ continue;
100
+ if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0 ||
101
+ hasModifier(statement, ts.SyntaxKind.DeclareKeyword)) {
102
+ continue;
103
+ }
104
+ for (const declaration of statement.declarationList.declarations) {
105
+ if (ts.isIdentifier(declaration.name) &&
106
+ declaration.name.text === name &&
107
+ declaration.initializer !== undefined &&
108
+ ts.isIdentifier(unwrapExpression(declaration.initializer)) &&
109
+ unwrapExpression(declaration.initializer).text ===
110
+ PLAYBOOK_FACTORY) {
111
+ return true;
112
+ }
113
+ }
114
+ }
115
+ return false;
116
+ }
117
+ function usesErasableTypeScript(file) {
118
+ let erasable = true;
119
+ const visit = (node) => {
120
+ if (ts.isEnumDeclaration(node) ||
121
+ ts.isModuleDeclaration(node) ||
122
+ ts.isImportEqualsDeclaration(node) ||
123
+ (ts.isExportAssignment(node) && node.isExportEquals) ||
124
+ (ts.isParameter(node) &&
125
+ ts.isParameterPropertyDeclaration(node, node.parent))) {
126
+ erasable = false;
127
+ return;
128
+ }
129
+ ts.forEachChild(node, visit);
130
+ };
131
+ visit(file);
132
+ return erasable;
133
+ }
134
+ function defaultExportCount(file) {
135
+ let count = 0;
136
+ for (const statement of file.statements) {
137
+ if ((ts.isFunctionDeclaration(statement) ||
138
+ ts.isClassDeclaration(statement)) &&
139
+ hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
140
+ count++;
141
+ }
142
+ else if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
143
+ count++;
144
+ }
145
+ else if (ts.isExportDeclaration(statement) &&
146
+ statement.exportClause !== undefined &&
147
+ ts.isNamedExports(statement.exportClause)) {
148
+ count += statement.exportClause.elements.filter((element) => !statement.isTypeOnly &&
149
+ !element.isTypeOnly &&
150
+ element.name.text === 'default').length;
151
+ }
152
+ }
153
+ return count;
154
+ }
155
+ function factoryValueBindingCount(file, name) {
156
+ let count = 0;
157
+ for (const statement of file.statements) {
158
+ if (ts.isFunctionDeclaration(statement) &&
159
+ statement.name?.text === name &&
160
+ statement.body !== undefined) {
161
+ count++;
162
+ }
163
+ if (ts.isVariableStatement(statement)) {
164
+ count += statement.declarationList.declarations.filter((declaration) => ts.isIdentifier(declaration.name) &&
165
+ declaration.name.text === name &&
166
+ declaration.initializer !== undefined).length;
167
+ }
168
+ }
169
+ return count;
170
+ }
171
+ function hasModifier(node, kind) {
172
+ return (ts.canHaveModifiers(node) &&
173
+ ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) === true);
174
+ }
175
+ function factoryBindingIsCallable(file, name, seen = new Set()) {
176
+ if (seen.has(name))
177
+ return false;
178
+ const nextSeen = new Set(seen).add(name);
179
+ for (const statement of file.statements) {
180
+ if (ts.isFunctionDeclaration(statement) &&
181
+ statement.name?.text === name &&
182
+ statement.body !== undefined &&
183
+ isSynchronousFunction(statement)) {
184
+ return true;
185
+ }
186
+ if (ts.isVariableStatement(statement)) {
187
+ for (const declaration of statement.declarationList.declarations) {
188
+ if (ts.isIdentifier(declaration.name) &&
189
+ declaration.name.text === name &&
190
+ declaration.initializer !== undefined &&
191
+ callableExpression(file, declaration.initializer, nextSeen)) {
192
+ return true;
193
+ }
194
+ }
195
+ }
196
+ }
197
+ return false;
198
+ }
199
+ function callableExpression(file, expression, seen) {
200
+ const value = unwrapExpression(expression);
201
+ if ((ts.isArrowFunction(value) || ts.isFunctionExpression(value)) &&
202
+ isSynchronousFunction(value)) {
203
+ return true;
204
+ }
205
+ if (ts.isIdentifier(value)) {
206
+ return factoryBindingIsCallable(file, value.text, seen);
207
+ }
208
+ // A DR-019 thin module binds the factory to a shared-engine factory call —
209
+ // `createXStatePlaybookRuntime(machine, spec)`. Static recognition accepts
210
+ // the call exactly when its callee is a non-type named import from the
211
+ // shared engine module; the loader confirms the returned factory's
212
+ // callability at run time (DR-017).
213
+ if (ts.isCallExpression(value)) {
214
+ const callee = unwrapExpression(value.expression);
215
+ if (ts.isIdentifier(callee)) {
216
+ return importedFromSharedEngine(file, callee.text);
217
+ }
218
+ }
219
+ return false;
220
+ }
221
+ const SHARED_ENGINE_MODULE = '@sublang/playbook/xstate-runtime';
222
+ /** True when `name` locally binds the shared engine's runtime factory export. */
223
+ function importedFromSharedEngine(file, name) {
224
+ for (const statement of file.statements) {
225
+ if (!ts.isImportDeclaration(statement))
226
+ continue;
227
+ if (!ts.isStringLiteral(statement.moduleSpecifier) ||
228
+ statement.moduleSpecifier.text !== SHARED_ENGINE_MODULE) {
229
+ continue;
230
+ }
231
+ const clause = statement.importClause;
232
+ if (clause === undefined || clause.phaseModifier !== undefined)
233
+ continue;
234
+ const bindings = clause.namedBindings;
235
+ if (bindings === undefined || !ts.isNamedImports(bindings))
236
+ continue;
237
+ for (const element of bindings.elements) {
238
+ if (!element.isTypeOnly &&
239
+ element.name.text === name &&
240
+ (element.propertyName?.text ?? element.name.text) ===
241
+ 'createXStatePlaybookRuntime') {
242
+ return true;
243
+ }
244
+ }
245
+ }
246
+ return false;
247
+ }
248
+ function isSynchronousFunction(node) {
249
+ return (!hasModifier(node, ts.SyntaxKind.AsyncKeyword) &&
250
+ (!('asteriskToken' in node) || node.asteriskToken === undefined));
251
+ }
252
+ function unwrapExpression(expression) {
253
+ let current = expression;
254
+ while (ts.isParenthesizedExpression(current) ||
255
+ ts.isTypeAssertionExpression(current) ||
256
+ ts.isAsExpression(current) ||
257
+ ts.isSatisfiesExpression(current) ||
258
+ ts.isNonNullExpression(current)) {
259
+ current = current.expression;
260
+ }
261
+ return current;
262
+ }
263
+ /**
264
+ * Seeds a phase request into the single non-interactive Boss turn `slc` hands
265
+ * the runtime through `handleBossInput` (PHEXEC-29; DR-005).
266
+ *
267
+ * The settled SLC-to-runtime seeding contract: one Boss turn whose text states
268
+ * the request kind in prose — so any compiled playbook's judge-backed classifier
269
+ * can route it — and carries the full request as a single-line JSON object
270
+ * introduced by `Request: `, with workspace paths already resolved to absolute
271
+ * host paths, so a runtime (or a deterministic fixture) recovers the exact
272
+ * `PhaseInput` without host-specific parsing.
273
+ */
274
+ export function seedPhaseTurn(input) {
275
+ const directive = input.kind === 'compile'
276
+ ? 'Perform this compile phase non-interactively: transform the source into the target artifact, then stop.'
277
+ : 'Perform this link phase non-interactively: link the object artifacts against the link target into the linked artifact, then stop.';
278
+ return `${directive}\nRequest: ${JSON.stringify(input)}`;
279
+ }
280
+ /**
281
+ * Composes the host workspace contract a transformation-performing direct
282
+ * Captain call carries (PHEXEC-34; DR-005, DR-012).
283
+ *
284
+ * A linked `playbook` artifact composes host-agnostic Captain prompts — the
285
+ * GEARS-derived domain body plus typed runtime evidence — and is forbidden from
286
+ * appending workspace specifics, which only the host owns. Mirroring the
287
+ * interpreted agent contract (PHEXEC-14), the host therefore supplies the
288
+ * request's absolute workspace paths and write-scope rules at its transport
289
+ * boundary, so the acting Captain writes exactly the declared target instead
290
+ * of merely emitting the artifact in its reply. Routing-only Captain calls
291
+ * (explicitly empty `allowedTools`) and hidden judge calls never carry it.
292
+ */
293
+ export function composeWorkspaceContract(input) {
294
+ const target = input.kind === 'compile' ? input.target : input.linked;
295
+ const inputs = input.kind === 'compile'
296
+ ? [`source to read: ${input.source}`]
297
+ : [
298
+ `object artifacts to read, in order: ${input.objects.join(', ')}`,
299
+ `link target module: ${input.linkTarget}`,
300
+ `options: ${formatWorkspaceOptions(input.options)}`,
301
+ ];
302
+ return [
303
+ 'Host workspace (SubLang Compiler):',
304
+ 'You are performing the work above non-interactively as one compiled phase of the SubLang Compiler (slc), against these workspace files:',
305
+ ...inputs.map((line) => `- ${line}`),
306
+ `- artifact to write: ${target}`,
307
+ '',
308
+ 'Workspace contract — you must:',
309
+ `- write only ${target}, creating or overwriting exactly that file; emitting the artifact solely in your reply does not produce it;`,
310
+ '- not edit the sources, the phase or link definition, specs, link targets, object artifacts, or any other file;',
311
+ '- not commit or otherwise touch version control;',
312
+ '- produce a complete artifact, not a sketch or placeholder;',
313
+ '- verify the produced artifact before finishing, then reply with a concise summary of what you produced and any ambiguity you resolved.',
314
+ ].join('\n');
315
+ }
316
+ function formatWorkspaceOptions(options) {
317
+ const entries = Object.entries(options);
318
+ if (entries.length === 0)
319
+ return '(none)';
320
+ return entries.map(([name, value]) => `${name}=${value}`).join(', ');
321
+ }
322
+ //# sourceMappingURL=phase-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-runner.js","sourceRoot":"","sources":["../src/phase-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /** A format token bound to its canonical file extension. */
2
+ export interface FormatDecl {
3
+ /** Short kebab-case language identifier, e.g. `text`, `gears`, `fsm`. */
4
+ format: string;
5
+ /** Canonical extension including the leading dot, e.g. `.md`, `.ts`. */
6
+ ext: string;
7
+ }
8
+ /** A loaded ordinary compile phase or format-preserving pass phase. */
9
+ export interface Phase {
10
+ /** Phase basename without the `.md` suffix, e.g. `text2gears` or `optimize`. */
11
+ name: string;
12
+ /** Declared source role. */
13
+ source: FormatDecl;
14
+ /** Declared target role. */
15
+ target: FormatDecl;
16
+ /**
17
+ * True for a format-preserving pass phase (source format equals target
18
+ * format), which sits outside the linear chain and runs only when a compile
19
+ * opts in (DR-013).
20
+ */
21
+ pass: boolean;
22
+ }
23
+ /** Machine-readable reason a `## Formats` table was refused. */
24
+ export type FormatsErrorCode = 'missing-formats' | 'malformed-formats' | 'missing-role';
25
+ /** Machine-readable reason a phase was refused. */
26
+ export type PhaseErrorCode = FormatsErrorCode | 'filename-mismatch' | 'extension-conflict';
27
+ /** Raised when a phase definition is refused under DR-001. */
28
+ export declare class PhaseError extends Error {
29
+ readonly code: PhaseErrorCode;
30
+ constructor(code: PhaseErrorCode, message: string);
31
+ }
32
+ /**
33
+ * Reads the authoritative `## Formats` table into its source and target
34
+ * declarations (PIPE-1), shared by the phase and link loaders.
35
+ *
36
+ * Refusals are reported through `fail` so each loader can raise its own error
37
+ * type; `fail` must not return.
38
+ */
39
+ export declare function readFormats(content: string, fail: (code: FormatsErrorCode, message: string) => never): {
40
+ source: FormatDecl;
41
+ target: FormatDecl;
42
+ };
43
+ /**
44
+ * Parses and validates a phase definition from its filename and content.
45
+ *
46
+ * @throws {PhaseError} when the `## Formats` table is missing or malformed, or
47
+ * when the filename does not match the declared tokens (PIPE-1, PIPE-2).
48
+ */
49
+ export declare function parsePhase(file: {
50
+ name: string;
51
+ content: string;
52
+ }): Phase;
53
+ /** Reads a phase definition file from disk and parses it (PIPE-1, PIPE-2). */
54
+ export declare function loadPhaseFile(path: string): Promise<Phase>;
55
+ /**
56
+ * Refuses a set of phases that declare conflicting extensions for the same
57
+ * format token (PIPE-3).
58
+ *
59
+ * @throws {PhaseError} with code `extension-conflict` on the first conflict.
60
+ */
61
+ export declare function checkExtensionConsistency(phases: readonly Phase[]): void;
62
+ //# sourceMappingURL=phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.d.ts","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"AAkBA,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,KAAK;IACpB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,4BAA4B;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,IAAI,EAAE,OAAO,CAAC;CACf;AAED,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,mBAAmB,GACnB,cAAc,CAAC;AAEnB,mDAAmD;AACnD,MAAM,MAAM,cAAc,GACtB,gBAAgB,GAChB,mBAAmB,GACnB,oBAAoB,CAAC;AAEzB,8DAA8D;AAC9D,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAElB,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM;CAKlD;AAKD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,GACvD;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAwC5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAmBzE;AAED,8EAA8E;AAC9E,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,IAAI,CAgBxE"}
package/dist/phase.js ADDED
@@ -0,0 +1,111 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Phase model and `## Formats` parsing (DR-001).
5
+ *
6
+ * Implements PIPE-1 (read the authoritative `## Formats` table), PIPE-2 (refuse
7
+ * a phase whose `<source-format>2<target-format>.md` filename does not match its
8
+ * table), and PIPE-3 (refuse phases declaring conflicting extensions for the
9
+ * same format token). The `## Formats` reader is shared with the link loader.
10
+ * See specs/dev/pipeline.md.
11
+ */
12
+ import { readFile } from 'node:fs/promises';
13
+ import { basename } from 'node:path';
14
+ import { findSection, parseTable } from './markdown.js';
15
+ /** Raised when a phase definition is refused under DR-001. */
16
+ export class PhaseError extends Error {
17
+ code;
18
+ constructor(code, message) {
19
+ super(message);
20
+ this.name = 'PhaseError';
21
+ this.code = code;
22
+ }
23
+ }
24
+ const FORMAT_TOKEN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
25
+ const EXTENSION = /^\.[A-Za-z0-9]+$/;
26
+ /**
27
+ * Reads the authoritative `## Formats` table into its source and target
28
+ * declarations (PIPE-1), shared by the phase and link loaders.
29
+ *
30
+ * Refusals are reported through `fail` so each loader can raise its own error
31
+ * type; `fail` must not return.
32
+ */
33
+ export function readFormats(content, fail) {
34
+ const section = findSection(content, 'Formats');
35
+ if (section === null) {
36
+ fail('missing-formats', 'missing a ## Formats section');
37
+ }
38
+ const decls = new Map();
39
+ for (const cells of parseTable(section)) {
40
+ if (cells.length < 3)
41
+ continue;
42
+ const role = cells[0].toLowerCase();
43
+ if (role !== 'source' && role !== 'target')
44
+ continue;
45
+ const [, format, ext] = cells;
46
+ if (!FORMAT_TOKEN.test(format)) {
47
+ fail('malformed-formats', `${role} format token "${format}" is not a kebab-case identifier`);
48
+ }
49
+ if (!EXTENSION.test(ext)) {
50
+ fail('malformed-formats', `${role} extension "${ext}" is not a canonical extension (e.g. ".md")`);
51
+ }
52
+ if (decls.has(role)) {
53
+ fail('malformed-formats', `## Formats declares ${role} more than once`);
54
+ }
55
+ decls.set(role, { format, ext });
56
+ }
57
+ const source = decls.get('source');
58
+ const target = decls.get('target');
59
+ if (source === undefined || target === undefined) {
60
+ fail('missing-role', `## Formats is missing a ${source === undefined ? 'source' : 'target'} row`);
61
+ }
62
+ return { source, target };
63
+ }
64
+ /**
65
+ * Parses and validates a phase definition from its filename and content.
66
+ *
67
+ * @throws {PhaseError} when the `## Formats` table is missing or malformed, or
68
+ * when the filename does not match the declared tokens (PIPE-1, PIPE-2).
69
+ */
70
+ export function parsePhase(file) {
71
+ const { source, target } = readFormats(file.content, (code, message) => {
72
+ throw new PhaseError(code, message);
73
+ });
74
+ // A format-preserving pass phase is named freely — its filename is the pass
75
+ // name (DR-013); the `<source>2<target>.md` rule identifies transform phases.
76
+ const pass = source.format === target.format;
77
+ if (!pass) {
78
+ const expected = `${source.format}2${target.format}.md`;
79
+ if (file.name !== expected) {
80
+ throw new PhaseError('filename-mismatch', `phase filename "${file.name}" does not match its ## Formats tokens (expected "${expected}")`);
81
+ }
82
+ }
83
+ return { name: file.name.slice(0, -'.md'.length), source, target, pass };
84
+ }
85
+ /** Reads a phase definition file from disk and parses it (PIPE-1, PIPE-2). */
86
+ export async function loadPhaseFile(path) {
87
+ const content = await readFile(path, 'utf8');
88
+ return parsePhase({ name: basename(path), content });
89
+ }
90
+ /**
91
+ * Refuses a set of phases that declare conflicting extensions for the same
92
+ * format token (PIPE-3).
93
+ *
94
+ * @throws {PhaseError} with code `extension-conflict` on the first conflict.
95
+ */
96
+ export function checkExtensionConsistency(phases) {
97
+ const seen = new Map();
98
+ for (const phase of phases) {
99
+ for (const decl of [phase.source, phase.target]) {
100
+ const prior = seen.get(decl.format);
101
+ if (prior === undefined) {
102
+ seen.set(decl.format, { ext: decl.ext, phase: phase.name });
103
+ }
104
+ else if (prior.ext !== decl.ext) {
105
+ throw new PhaseError('extension-conflict', `format "${decl.format}" declares conflicting extensions: ` +
106
+ `"${prior.ext}" (in ${prior.phase}) vs "${decl.ext}" (in ${phase.name})`);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ //# sourceMappingURL=phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.js","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { PinRecord } from './pins.js';
2
+ /** Extracts the inline-code paths cited by a `## Pin Inputs` section (DR-007). */
3
+ export declare function parsePinInputs(content: string): string[];
4
+ /**
5
+ * Derives the semantic-input closure of `definitionPath` as a set of resolved
6
+ * absolute paths: the definition plus every transitively cited local file
7
+ * (PIN-2, PIN-4).
8
+ *
9
+ * @throws {import('./pins.js').PinError} when a cited path is absolute or escapes
10
+ * the recorded boundary (PIN-5).
11
+ */
12
+ export declare function deriveClosure(pipelineDir: string, boundary: string, definitionPath: string): Promise<Set<string>>;
13
+ /**
14
+ * Reports whether the closure derived from `record.definition` matches the
15
+ * recorded definition plus semanticInputs, compared as sets of resolved paths
16
+ * (PIN-4).
17
+ */
18
+ export declare function closureMatchesRecord(pipelineDir: string, boundary: string, record: PinRecord): Promise<boolean>;
19
+ //# sourceMappingURL=pin-closure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pin-closure.d.ts","sourceRoot":"","sources":["../src/pin-closure.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAK3C,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAYxD;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAoCtB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,OAAO,CAAC,CAqBlB"}
@@ -0,0 +1,110 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Semantic-input closure derivation for the pin-currency validator (PIN-2, PIN-4;
5
+ * DR-007).
6
+ *
7
+ * A pinned phase's semantic-input closure is the definition file plus every local
8
+ * file transitively cited by a `## Pin Inputs` section. A `## Pin Inputs` section
9
+ * cites each input as an inline-code path (for example `` `reference/gears.md` ``)
10
+ * expressed relative to the pipeline directory — the same coordinate system as
11
+ * the recorded pin paths. Derivation recurses only into Markdown inputs that
12
+ * declare their own `## Pin Inputs`; a non-Markdown input, or a Markdown input
13
+ * without the section, is a closure member but terminates the transitive walk.
14
+ * The derived closure is compared — as a set of resolved paths — to the recorded
15
+ * definition plus semanticInputs; any difference is a stale verdict (PIN-4). See
16
+ * specs/dev/pinning.md.
17
+ */
18
+ import { readFile } from 'node:fs/promises';
19
+ import { findSection } from './markdown.js';
20
+ import { resolvePinPath } from './pin-paths.js';
21
+ const PIN_INPUT_FIELD = 'pin input path';
22
+ const INLINE_CODE = /`([^`]+)`/g;
23
+ /** Extracts the inline-code paths cited by a `## Pin Inputs` section (DR-007). */
24
+ export function parsePinInputs(content) {
25
+ const lines = findSection(content, 'Pin Inputs');
26
+ if (lines === null) {
27
+ return [];
28
+ }
29
+ const paths = [];
30
+ for (const line of lines) {
31
+ for (const match of line.matchAll(INLINE_CODE)) {
32
+ paths.push(match[1].trim());
33
+ }
34
+ }
35
+ return paths;
36
+ }
37
+ /**
38
+ * Derives the semantic-input closure of `definitionPath` as a set of resolved
39
+ * absolute paths: the definition plus every transitively cited local file
40
+ * (PIN-2, PIN-4).
41
+ *
42
+ * @throws {import('./pins.js').PinError} when a cited path is absolute or escapes
43
+ * the recorded boundary (PIN-5).
44
+ */
45
+ export async function deriveClosure(pipelineDir, boundary, definitionPath) {
46
+ const closure = new Set();
47
+ const seen = new Set();
48
+ const queue = [definitionPath];
49
+ while (queue.length > 0) {
50
+ const rel = queue.shift();
51
+ if (seen.has(rel)) {
52
+ continue;
53
+ }
54
+ seen.add(rel);
55
+ const resolved = resolvePinPath(pipelineDir, boundary, rel, PIN_INPUT_FIELD);
56
+ closure.add(resolved);
57
+ // Recurse only into Markdown inputs that declare their own ## Pin Inputs;
58
+ // non-Markdown and sectionless inputs are members but terminate the walk.
59
+ if (!isMarkdown(rel)) {
60
+ continue;
61
+ }
62
+ const content = await readIfPresent(resolved);
63
+ if (content === null) {
64
+ continue;
65
+ }
66
+ for (const cited of parsePinInputs(content)) {
67
+ if (!seen.has(cited)) {
68
+ queue.push(cited);
69
+ }
70
+ }
71
+ }
72
+ return closure;
73
+ }
74
+ /**
75
+ * Reports whether the closure derived from `record.definition` matches the
76
+ * recorded definition plus semanticInputs, compared as sets of resolved paths
77
+ * (PIN-4).
78
+ */
79
+ export async function closureMatchesRecord(pipelineDir, boundary, record) {
80
+ const derived = await deriveClosure(pipelineDir, boundary, record.definition.path);
81
+ const recorded = new Set();
82
+ recorded.add(resolvePinPath(pipelineDir, boundary, record.definition.path, 'definition.path'));
83
+ for (const input of record.semanticInputs) {
84
+ recorded.add(resolvePinPath(pipelineDir, boundary, input.path, 'semanticInputs.path'));
85
+ }
86
+ return setsEqual(derived, recorded);
87
+ }
88
+ function isMarkdown(path) {
89
+ return path.toLowerCase().endsWith('.md');
90
+ }
91
+ async function readIfPresent(absolutePath) {
92
+ try {
93
+ return await readFile(absolutePath, 'utf8');
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ function setsEqual(a, b) {
100
+ if (a.size !== b.size) {
101
+ return false;
102
+ }
103
+ for (const value of a) {
104
+ if (!b.has(value)) {
105
+ return false;
106
+ }
107
+ }
108
+ return true;
109
+ }
110
+ //# sourceMappingURL=pin-closure.js.map