@orkestrel/scaffold 0.0.2 → 0.0.4

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 (82) hide show
  1. package/README.md +29 -15
  2. package/dist/bin/scaffold.js +1162 -1200
  3. package/dist/bin/scaffold.js.map +1 -1
  4. package/dist/host/AGENTS.md +124 -925
  5. package/dist/host/CLAUDE.md +335 -495
  6. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +53 -0
  7. package/dist/host/agents/skills/orkestrel-align-packages/agents/openai.yaml +4 -0
  8. package/dist/host/agents/skills/orkestrel-align-packages/references/fleet.md +50 -0
  9. package/dist/host/agents/skills/orkestrel-align-packages/references/integration.md +56 -0
  10. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +64 -0
  11. package/dist/host/agents/skills/orkestrel-build-application/agents/openai.yaml +4 -0
  12. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +129 -0
  13. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +64 -0
  14. package/dist/host/agents/skills/orkestrel-harden-package/agents/openai.yaml +4 -0
  15. package/dist/host/agents/skills/orkestrel-harden-package/references/centralization.md +85 -0
  16. package/dist/host/agents/skills/orkestrel-harden-package/references/contract.md +57 -0
  17. package/dist/host/agents/skills/orkestrel-harden-package/references/hardening.md +93 -0
  18. package/dist/host/agents/skills/orkestrel-harden-package/references/research.md +61 -0
  19. package/dist/host/claude/agents/application.md +30 -0
  20. package/dist/host/claude/agents/builder.md +10 -7
  21. package/dist/host/claude/agents/checker.md +14 -8
  22. package/dist/host/claude/agents/codex.md +94 -0
  23. package/dist/host/claude/agents/grok.md +37 -34
  24. package/dist/host/claude/agents/implementer.md +40 -0
  25. package/dist/host/claude/agents/orkestrel.md +88 -223
  26. package/dist/host/claude/agents/planner.md +13 -34
  27. package/dist/host/claude/agents/reviewer.md +38 -24
  28. package/dist/host/claude/agents/verifier.md +14 -10
  29. package/dist/host/claude/rules/application.md +57 -0
  30. package/dist/host/claude/rules/architecture.md +180 -0
  31. package/dist/host/claude/rules/browser.md +28 -0
  32. package/dist/host/claude/rules/documentation.md +64 -0
  33. package/dist/host/claude/rules/names.md +209 -0
  34. package/dist/host/claude/rules/patterns.md +130 -0
  35. package/dist/host/claude/rules/quality.md +45 -0
  36. package/dist/host/claude/rules/styles.md +64 -0
  37. package/dist/host/claude/rules/tests.md +123 -0
  38. package/dist/host/claude/rules/typescript.md +78 -0
  39. package/dist/host/claude/rules/workspace.md +180 -0
  40. package/dist/host/claude/settings.json +112 -0
  41. package/dist/host/claude/skills/orkestrel-align-packages/SKILL.md +10 -0
  42. package/dist/host/claude/skills/orkestrel-build-application/SKILL.md +12 -0
  43. package/dist/host/claude/skills/orkestrel-harden-package/SKILL.md +10 -0
  44. package/dist/host/codex/agents/analyst.toml +15 -0
  45. package/dist/host/codex/agents/application.toml +25 -0
  46. package/dist/host/codex/agents/builder.toml +24 -0
  47. package/dist/host/codex/agents/checker.toml +15 -0
  48. package/dist/host/codex/agents/grok.toml +17 -0
  49. package/dist/host/codex/agents/implementer.toml +17 -0
  50. package/dist/host/codex/agents/opus.toml +25 -0
  51. package/dist/host/codex/agents/orkestrel.toml +16 -0
  52. package/dist/host/codex/agents/planner.toml +20 -0
  53. package/dist/host/codex/agents/reviewer.toml +18 -0
  54. package/dist/host/codex/agents/verifier.toml +14 -0
  55. package/dist/host/codex/config.toml +66 -0
  56. package/dist/host/cursor/mcp.json +12 -0
  57. package/dist/host/dotfiles/mcp.json +8 -0
  58. package/dist/host/dotfiles/oxlintrc.json +307 -1
  59. package/dist/host/guides/src/scaffold.md +2059 -2007
  60. package/dist/host/manifest.json +376 -137
  61. package/dist/host/scripts/codex.sh +49 -0
  62. package/dist/host/scripts/cursor.sh +33 -47
  63. package/dist/host/scripts/deps.sh +34 -16
  64. package/dist/host/scripts/ollama.sh +6 -149
  65. package/dist/host/tests/setupPolicy.ts +390 -0
  66. package/dist/src/core/index.cjs +6603 -1200
  67. package/dist/src/core/index.cjs.map +1 -1
  68. package/dist/src/core/index.d.cts +802 -267
  69. package/dist/src/core/index.d.ts +802 -267
  70. package/dist/src/core/index.js +6521 -1192
  71. package/dist/src/core/index.js.map +1 -1
  72. package/dist/src/server/index.cjs +3132 -593
  73. package/dist/src/server/index.cjs.map +1 -1
  74. package/dist/src/server/index.d.cts +643 -95
  75. package/dist/src/server/index.d.ts +643 -95
  76. package/dist/src/server/index.js +3054 -594
  77. package/dist/src/server/index.js.map +1 -1
  78. package/package.json +16 -12
  79. package/dist/host/claude/agents/composer.md +0 -64
  80. package/dist/host/claude/agents/researcher.md +0 -38
  81. package/dist/host/claude/agents/scout.md +0 -35
  82. package/dist/host/github/workflows/ci.yml +0 -64
@@ -1,15 +1,27 @@
1
1
  import { Audit } from '../core/index.ts';
2
2
  import { Blueprint } from '../core/index.ts';
3
+ import { BooleanShape } from '@orkestrel/contract';
3
4
  import { CatalogEntry } from '../core/index.ts';
5
+ import { ContractInterface } from '@orkestrel/contract';
4
6
  import { Dependency } from '../core/index.ts';
5
7
  import { EmitterErrorHandler } from '@orkestrel/emitter';
6
8
  import { EmitterHooks } from '@orkestrel/emitter';
7
9
  import { EmitterInterface } from '@orkestrel/emitter';
8
10
  import { GuideSync } from '../core/index.ts';
11
+ import { HostArtifact } from '../core/index.ts';
12
+ import { NumberShape } from '@orkestrel/contract';
13
+ import { ObjectShape } from '@orkestrel/contract';
14
+ import { OptionalShape } from '@orkestrel/contract';
9
15
  import { Plan } from '../core/index.ts';
16
+ import { ScaffoldErrorCode } from '../core/index.ts';
17
+ import { Snapshot } from '../core/index.ts';
18
+ import { StringShape } from '@orkestrel/contract';
10
19
  import { SyncReport } from '../core/index.ts';
11
20
  import { VersionSync } from '../core/index.ts';
12
21
 
22
+ /** One mutable aggregate entry allowance shared by fleet catalog roots. */
23
+ export declare type CatalogAllowance = Float64Array;
24
+
13
25
  /**
14
26
  * Build the fleet package catalog — one `CatalogEntry` per `@orkestrel/*`
15
27
  * package discovered under each root, its description drawn from its own
@@ -42,7 +54,20 @@ import { VersionSync } from '../core/index.ts';
42
54
  * catalogPackages(['/repos']) // [{ name: '@orkestrel/contract', version: '0.0.5', description: '…' }, …]
43
55
  * ```
44
56
  */
45
- export declare function catalogPackages(roots: readonly string[]): readonly CatalogEntry[];
57
+ export declare function catalogPackages(roots: readonly string[], limit?: number): readonly CatalogEntry[];
58
+
59
+ /**
60
+ * Promote a complete staged set and roll every earlier destination back when
61
+ * any later promotion fails.
62
+ *
63
+ * @param transaction - Same-volume sibling staging state.
64
+ * @param paths - Portable target-relative files to promote.
65
+ * @throws `ScaffoldError('WRITE', ...)` with recovery details on failure.
66
+ */
67
+ export declare function commitWriteTransaction(transaction: WriteTransaction, paths: readonly string[]): void;
68
+
69
+ /** Consume one aggregate fleet-catalog traversal slot. */
70
+ export declare function consumeCatalogAllowance(allowance: CatalogAllowance, root: string): void;
46
71
 
47
72
  /**
48
73
  * Create a `MaterializerInterface` (server) — the materialization entity,
@@ -78,6 +103,30 @@ export declare function createMaterializer(options?: MaterializerOptions): Mater
78
103
  */
79
104
  export declare function createSync(options?: SyncOptions): SyncInterface;
80
105
 
106
+ /**
107
+ * Create a physical directory path one segment at a time behind captured identities.
108
+ *
109
+ * @param path - The absolute directory path to establish.
110
+ * @param boundary - The boundary name used in diagnostics.
111
+ * @returns The final directory anchor and the subset created by this call.
112
+ */
113
+ export declare function createWriteDirectory(path: string, boundary: string): WriteDirectoryResult;
114
+
115
+ /** Default cumulative response bytes retained by one Sync operation. */
116
+ export declare const DEFAULT_SYNC_BUDGET = 16777216;
117
+
118
+ /** Default simultaneous upstream requests used by `Sync`. */
119
+ export declare const DEFAULT_SYNC_CONCURRENCY = 6;
120
+
121
+ /** Default maximum dependencies or catalog entries retained by one Sync operation. */
122
+ export declare const DEFAULT_SYNC_ITEMS = 256;
123
+
124
+ /** Default per-response byte limit used by `Sync`. */
125
+ export declare const DEFAULT_SYNC_LIMIT = 5242880;
126
+
127
+ /** Default per-request timeout used by `Sync`, in milliseconds. */
128
+ export declare const DEFAULT_SYNC_TIMEOUT = 10000;
129
+
81
130
  /**
82
131
  * Reconstruct a `Blueprint` from an EXISTING repo at `target` — the faithful
83
132
  * inverse `audit` / `repair` / `mirror` need to diff a live package against
@@ -85,12 +134,12 @@ export declare function createSync(options?: SyncOptions): SyncInterface;
85
134
  *
86
135
  * @param target - The existing package directory to derive a `Blueprint` from.
87
136
  * @remarks
88
- * `name` strips the `@orkestrel/` prefix off `manifest.name` a non-`@orkestrel`
89
- * name is a coded `TARGET` failure, since this tool derives only `@orkestrel`
90
- * packages. `surfaces` is read off the LIVE line: every surface the package
91
- * carries has a `src/<surface>/` directory, so each of `'core' | 'browser' |
92
- * 'server'` is included iff that directory exists at `target`; a target with
93
- * NONE of the three is also a coded `TARGET` failure. `dependencies` /
137
+ * A scoped package name is stripped to its bounded safe short name. An
138
+ * unscoped name is accepted only when the target is app-only, `private: true`,
139
+ * and the name satisfies `isWorkspaceName`; every other name is a coded
140
+ * `TARGET` failure. `src` is derived from `src/<environment>/` and `app` from
141
+ * `app/<environment>/`; a target with no environment on either axis is also a coded
142
+ * `TARGET` failure. `dependencies` /
94
143
  * `peers` are the `@orkestrel/`-prefixed entries of `manifest.dependencies` /
95
144
  * `manifest.peerDependencies` (a peer flagged `peerDependenciesMeta[name]
96
145
  * .optional === true` carries `optional: true`). `extras` is EVERY entry of
@@ -102,24 +151,56 @@ export declare function createSync(options?: SyncOptions): SyncInterface;
102
151
  * ALSO present in
103
152
  * `manifest.peerDependencies` or `manifest.dependencies` (e.g. a peer
104
153
  * dev-installed for local testing) is likewise excluded from `extras` — it
105
- * already surfaces as a `peer`/`dependency` above, and double-counting it as
154
+ * already appears as a `peer`/`dependency` above, and double-counting it as
106
155
  * an `extra` would land it in `peers ∩ extras`, a blocking `validateBlueprint`
107
156
  * gate. `overrides` is always `[]` — derivation cannot know a caller's
108
157
  * template-override intent.
109
158
  * @returns The reconstructed `Blueprint`.
110
159
  * @throws `ScaffoldError('TARGET', …)` when `target`'s manifest is unreadable
111
- * (via `readManifest`), is not valid JSON, its `name` is not `@orkestrel`-
112
- * prefixed, or `target` carries none of the three surface directories.
160
+ * (via `readManifest`), is not valid JSON, its name is unsafe for its
161
+ * publication mode, or `target` carries no source or application environment.
113
162
  *
114
163
  * @example
115
164
  * ```ts
116
165
  * import { deriveBlueprint } from '@orkestrel/scaffold/server'
117
166
  *
118
- * deriveBlueprint('./packages/router') // { name: 'router', surfaces: ['core', 'server'], … }
167
+ * deriveBlueprint('./packages/router') // { name: 'router', src: ['core', 'server'], … }
119
168
  * ```
120
169
  */
121
170
  export declare function deriveBlueprint(target: string): Blueprint;
122
171
 
172
+ /**
173
+ * Compute a bounded-memory SHA-256 digest for one file.
174
+ *
175
+ * @param path - The file to read.
176
+ * @returns Its lowercase SHA-256 digest.
177
+ */
178
+ export declare function digestFile(path: string): string;
179
+
180
+ /**
181
+ * Compute SHA-256 from exact lowercase hexadecimal bytes without decoding the whole value at once.
182
+ *
183
+ * @param hex - Exact hexadecimal bytes.
184
+ * @returns Their lowercase SHA-256 digest.
185
+ */
186
+ export declare function digestHex(hex: string): string;
187
+
188
+ /**
189
+ * Compute SHA-256 from UTF-8 text.
190
+ *
191
+ * @param value - The text to hash.
192
+ * @returns Its lowercase SHA-256 digest.
193
+ */
194
+ export declare function digestText(value: string): string;
195
+
196
+ /**
197
+ * Remove an uncommitted or already-committed write transaction's private residue.
198
+ *
199
+ * @param transaction - The transaction to discard.
200
+ * @throws `ScaffoldError('WRITE', ...)` when its private root cannot be removed.
201
+ */
202
+ export declare function discardWriteTransaction(transaction: WriteTransaction): void;
203
+
123
204
  /**
124
205
  * List a fleet root's `@orkestrel/*` package directories.
125
206
  *
@@ -137,7 +218,47 @@ export declare function deriveBlueprint(target: string): Blueprint;
137
218
  * discoverPackages('./packages') // ['/abs/packages/router', '/abs/packages/budget']
138
219
  * ```
139
220
  */
140
- export declare function discoverPackages(root: string): readonly string[];
221
+ export declare function discoverPackages(root: string, allowance?: CatalogAllowance): readonly string[];
222
+
223
+ /**
224
+ * Render the local pointer written when an upstream dependency guide is not vendored yet.
225
+ *
226
+ * @param source - The canonical `guides/src/<name>.md` source path.
227
+ * @returns The deterministic pointer content.
228
+ */
229
+ export declare function guideStub(source: string): string;
230
+
231
+ /**
232
+ * Extract the first paragraph from the first blockquote in a Markdown guide.
233
+ *
234
+ * @param text - The guide Markdown to traverse.
235
+ * @returns A normalized one-line description, or `undefined` when parsing
236
+ * fails, no blockquote paragraph exists, or the paragraph is empty.
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * import { guideToDescription } from '@orkestrel/scaffold/server'
241
+ *
242
+ * guideToDescription('> A concise package description.\n>\n> More detail.')
243
+ * // 'A concise package description.'
244
+ * ```
245
+ */
246
+ export declare function guideToDescription(text: string): string | undefined;
247
+
248
+ /** One validated guide update and its contained destination. */
249
+ export declare interface GuideWrite {
250
+ readonly guide: GuideSync;
251
+ readonly destination: string;
252
+ }
253
+
254
+ /** Reserved metadata file written at the root of every staged host. */
255
+ export declare const HOST_MANIFEST_PATH = "manifest.json";
256
+
257
+ /** A complete vendored-host inventory with file entries and declared directory roots. */
258
+ export declare interface HostManifest {
259
+ readonly entries: readonly ManifestEntry[];
260
+ readonly roots: readonly string[];
261
+ }
141
262
 
142
263
  /**
143
264
  * Locate this MODULE's own installed package root — the nearest ancestor of
@@ -164,16 +285,18 @@ export declare function discoverPackages(root: string): readonly string[];
164
285
  export declare function hostRoot(): string;
165
286
 
166
287
  /**
167
- * Rehydrate a `Plan`'s `host`-origin artifacts with their real byte content
168
- * read from `host` — manifest-aware, via `locateHostSource`.
288
+ * Rehydrate a `Plan`'s `host`-origin artifacts with their exact byte hex read
289
+ * from `host` — manifest-aware, via `locateHostSource`.
169
290
  *
170
291
  * @param plan - The plan to hydrate.
171
292
  * @param host - The resolved host root to read from.
172
- * @returns A new `Plan` whose file-shaped `host` artifacts carry `content`;
173
- * `template` / `computed` artifacts and directory-shaped `host` artifacts
174
- * (no single storage file to read) pass through untouched.
175
- * @throws `ScaffoldError('TARGET', …)` when a resolved, existing host source
176
- * file fails to read.
293
+ * @returns A new `Plan` whose file-shaped `host` artifacts carry exact `hex`.
294
+ * Directory-shaped host artifacts expand into one byte-aware artifact
295
+ * per file, preserving their group and mapping the artifact/source prefixes.
296
+ * `template` / `computed` artifacts pass through untouched.
297
+ * @throws `ScaffoldError('TARGET', …)` when the host is not a readable
298
+ * directory, a present manifest is invalid/incomplete, or a required source
299
+ * is absent, escaping, not a file/directory as declared, or unreadable.
177
300
  *
178
301
  * @example
179
302
  * ```ts
@@ -184,38 +307,77 @@ export declare function hostRoot(): string;
184
307
  */
185
308
  export declare function hydratePlan(plan: Plan, host: string): Plan;
186
309
 
310
+ /** Narrow one exact aggregate fleet traversal allowance. */
311
+ export declare function isCatalogAllowance(value: unknown): value is CatalogAllowance;
312
+
313
+ /** Whether a normalized catalog description is bounded and contains no controls. */
314
+ export declare function isCatalogDescription(value: unknown): value is string;
315
+
316
+ /** Whether one dependency is an exact data-property record safe to snapshot. */
317
+ export declare function isDependencyData(value: unknown): value is Dependency;
318
+
319
+ /** Whether one host filesystem path is bounded, non-empty, and safe to render in a terminal. */
320
+ export declare function isFilesystemPath(value: unknown): value is string;
321
+
187
322
  /**
188
- * Whether `value` is a well-formed `host/manifest.json` entry — a string
189
- * `storage`, a string `destination`, and a boolean `executable`.
323
+ * Narrow a value to one exact complete vendored-host manifest.
190
324
  *
191
- * @param value - The candidate raw manifest entry.
192
- * @returns `true` when `value` narrows to `ManifestEntry`.
193
- *
194
- * @example
195
- * ```ts
196
- * import { isManifestEntry } from '@orkestrel/scaffold/server'
325
+ * @param value - The candidate manifest value.
326
+ * @returns `true` only for an exact `{ entries, roots }` record with safe paths.
327
+ */
328
+ export declare function isHostManifest(value: unknown): value is HostManifest;
329
+
330
+ /**
331
+ * Narrow a value to one exact vendored-host manifest entry.
197
332
  *
198
- * isManifestEntry({ storage: 'a', destination: 'b', executable: false }) // true
199
- * isManifestEntry({ storage: 'a', destination: 'b' }) // false — missing `executable`
200
- * ```
333
+ * @param value - The candidate raw manifest entry.
334
+ * @returns `true` only for the exact safe entry shape.
201
335
  */
202
336
  export declare function isManifestEntry(value: unknown): value is ManifestEntry;
203
337
 
338
+ /** Narrow an exact initial-listener record for `Materializer`. */
339
+ export declare function isMaterializerEventHooks(value: unknown): value is EmitterHooks<MaterializerEventMap>;
340
+
204
341
  /**
205
- * Whether `value` is a plain object (not `null`, not an array).
342
+ * Determine whether a caught filesystem error reports an absent path.
206
343
  *
207
- * @param value - The candidate value.
208
- * @returns `true` when `value` narrows to `Record<string, unknown>`.
344
+ * @param value - The caught value.
345
+ * @returns `true` only for an `Error` whose `code` is exactly `ENOENT`.
346
+ */
347
+ export declare function isMissingPathError(value: unknown): boolean;
348
+
349
+ /**
350
+ * Determine whether a value is a non-empty portable relative POSIX path.
209
351
  *
210
- * @example
211
- * ```ts
212
- * import { isRecord } from '@orkestrel/scaffold/server'
352
+ * @param value - The candidate path.
353
+ * @returns `true` when every path segment is safe and portable.
354
+ */
355
+ export declare function isPortablePath(value: unknown): value is string;
356
+
357
+ /**
358
+ * Whether a path is an existing physical directory rather than a file or link.
213
359
  *
214
- * isRecord({ a: 1 }) // true
215
- * isRecord(null) // false
216
- * ```
360
+ * @param path - The filesystem path to inspect without following links.
361
+ * @returns `true` only for a successful `lstat` reporting a directory.
362
+ */
363
+ export declare function isRealDirectory(path: string): boolean;
364
+
365
+ /** Determine whether a target-relative path addresses preserved repository metadata. */
366
+ export declare function isReservedTargetPath(value: string): boolean;
367
+
368
+ /**
369
+ * Determine whether a host-relative path resembles local configuration or credentials.
370
+ *
371
+ * @param value - The portable candidate path.
372
+ * @returns `true` when the path must be excluded from vendored host output.
217
373
  */
218
- export declare function isRecord(value: unknown): value is Record<string, unknown>;
374
+ export declare function isSensitiveHostPath(value: string): boolean;
375
+
376
+ /** Narrow an exact initial-listener record for `Sync`. */
377
+ export declare function isSyncEventHooks(value: unknown): value is EmitterHooks<SyncEventMap>;
378
+
379
+ /** Whether one externally supplied string is safe to render in terminal or JSON diagnostics. */
380
+ export declare function isTerminalText(value: unknown): value is string;
219
381
 
220
382
  /**
221
383
  * Whether a target path is absent, empty, or contains nothing but a `.git`
@@ -233,12 +395,23 @@ export declare function isRecord(value: unknown): value is Record<string, unknow
233
395
  */
234
396
  export declare function isVacant(target: string): boolean;
235
397
 
398
+ /** Narrow one exact transaction destination precondition. */
399
+ export declare function isWritePrecondition(value: unknown): value is WritePrecondition;
400
+
401
+ /**
402
+ * Recursively list a directory's descendant directories.
403
+ *
404
+ * @param root - The directory to list.
405
+ * @returns Root-relative POSIX directory paths in code-unit order.
406
+ */
407
+ export declare function listDirectories(root: string): readonly string[];
408
+
236
409
  /**
237
410
  * Recursively list a directory's files as root-relative paths.
238
411
  *
239
412
  * @param root - The directory to list.
240
- * @returns Root-relative file paths (posix-style `/` separators), or `[]`
241
- * when `root` is absent.
413
+ * @returns Root-relative file paths (posix-style `/` separators), code-unit
414
+ * sorted, or `[]` when `root` is absent.
242
415
  *
243
416
  * @example
244
417
  * ```ts
@@ -253,7 +426,7 @@ export declare function listFiles(root: string): readonly string[];
253
426
  * Resolve the absolute host-storage path for a host-origin artifact's
254
427
  * `source`, manifest-aware.
255
428
  *
256
- * @param manifest - The host's parsed `manifest.json` entries, or `undefined`
429
+ * @param manifest - The parsed complete host manifest, or `undefined`
257
430
  * when the host carries none (raw-repo-root fallback).
258
431
  * @param source - The artifact's `source` (or `path`) to resolve.
259
432
  * @param host - The resolved host root the path is joined against.
@@ -269,11 +442,18 @@ export declare function listFiles(root: string): readonly string[];
269
442
  * import { locateHostSource } from '@orkestrel/scaffold/server'
270
443
  *
271
444
  * locateHostSource(undefined, 'package.json', './dist/host') // './dist/host/package.json'
272
- * locateHostSource([{ storage: 'pkg.tmpl', destination: 'package.json', executable: false }], 'package.json', './dist/host')
445
+ * locateHostSource(
446
+ * {
447
+ * entries: [{ storage: 'pkg.tmpl', destination: 'package.json', executable: false }],
448
+ * roots: [],
449
+ * },
450
+ * 'package.json',
451
+ * './dist/host',
452
+ * )
273
453
  * // './dist/host/pkg.tmpl'
274
454
  * ```
275
455
  */
276
- export declare function locateHostSource(manifest: readonly ManifestEntry[] | undefined, source: string, host: string): string | undefined;
456
+ export declare function locateHostSource(manifest: HostManifest | undefined, source: string, host: string): string | undefined;
277
457
 
278
458
  /** One entry of the vendored host's `manifest.json` (server). */
279
459
  export declare interface ManifestEntry {
@@ -283,7 +463,7 @@ export declare interface ManifestEntry {
283
463
  }
284
464
 
285
465
  /**
286
- * The materialization entity (server) — the only impure surface in the
466
+ * The materialization entity (server) — the only impure environment in the
287
467
  * package, writing a `Plan` to `node:fs` behind an explicit call.
288
468
  *
289
469
  * @remarks
@@ -293,10 +473,16 @@ export declare interface ManifestEntry {
293
473
  * `content`, failing fast on any write error (`ScaffoldError('WRITE', …)`).
294
474
  * `repair` is into-existing: it skips the vacancy check and writes ONLY the
295
475
  * `missing` / `stale` artifacts an `Audit` names, leaving `aligned` ones
296
- * untouched. `prune` deletes stale files under `target/.claude/agents/` and
297
- * `target/scripts/` that the vendored `host` no longer names — the retired
298
- * `mirror.sh`/`scaffold.sh` cleanup step, now a method. After `destroy()`
476
+ * untouched. Hydrated directory-shaped host entries are expanded into
477
+ * file-shaped artifacts, so canonical skills and agent configuration are
478
+ * audited and repaired file by file. `prune` deletes stale files under
479
+ * `target/.claude/agents/`, `target/.codex/agents/`, and `target/scripts/`
480
+ * that the vendored `host` does not name. After `destroy()`
299
481
  * every method throws `DESTROYED`; teardown is idempotent, emitter last.
482
+ * Before the vacancy check or any write, `materialize` and `repair` require a
483
+ * structural `isPlan` match and a valid `validatePlan` result. Contextual
484
+ * overrides that are missing from the artifact set, host-owned, or target
485
+ * `package.json` therefore fail closed instead of being silently ignored.
300
486
  *
301
487
  * @remarks
302
488
  * `host`-origin copies are MANIFEST-AWARE: when the resolved `host` root
@@ -356,7 +542,7 @@ export declare interface ManifestEntry {
356
542
  * import { blueprint, blueprintToPlan } from '@orkestrel/scaffold'
357
543
  * import { createMaterializer } from '@orkestrel/scaffold/server'
358
544
  *
359
- * const plan = blueprintToPlan(blueprint('budget', { surfaces: ['core'] }))
545
+ * const plan = blueprintToPlan(blueprint('budget', { src: ['core'] }))
360
546
  * const materializer = createMaterializer()
361
547
  * materializer.materialize(plan, './packages/budget-new')
362
548
  * materializer.destroy()
@@ -368,7 +554,7 @@ export declare class Materializer implements MaterializerInterface {
368
554
  get emitter(): EmitterInterface<MaterializerEventMap>;
369
555
  materialize(plan: Plan, target: string): MaterializeResult;
370
556
  repair(plan: Plan, audit: Audit, target: string): MaterializeResult;
371
- prune(target: string): MaterializeResult;
557
+ prune(target: string, expected: Readonly<Record<string, string>>): MaterializeResult;
372
558
  destroy(): void;
373
559
  }
374
560
 
@@ -381,7 +567,7 @@ export declare interface MaterializeResult {
381
567
  readonly removed: readonly string[];
382
568
  }
383
569
 
384
- /** `Materializer`'s push observation surface (AGENTS §13, server). */
570
+ /** `Materializer`'s push observation channel (AGENTS §13, server). */
385
571
  export declare type MaterializerEventMap = {
386
572
  readonly copy: readonly [path: string];
387
573
  readonly write: readonly [path: string];
@@ -396,7 +582,7 @@ export declare interface MaterializerInterface {
396
582
  readonly emitter: EmitterInterface<MaterializerEventMap>;
397
583
  materialize(plan: Plan, target: string): MaterializeResult;
398
584
  repair(plan: Plan, audit: Audit, target: string): MaterializeResult;
399
- prune(target: string): MaterializeResult;
585
+ prune(target: string, expected: Snapshot): MaterializeResult;
400
586
  destroy(): void;
401
587
  }
402
588
 
@@ -418,23 +604,123 @@ export declare interface MaterializerOptions {
418
604
  readonly error?: EmitterErrorHandler;
419
605
  }
420
606
 
607
+ /** The compiled, closed data-only `MaterializerOptions` contract. */
608
+ export declare const materializerOptionsContract: ContractInterface<Readonly<{} & {
609
+ host?: string;
610
+ }>>;
611
+
612
+ /** Build the closed data-only `MaterializerOptions` shape. */
613
+ export declare function materializerOptionsShape(): ObjectShape<{
614
+ host: OptionalShape<StringShape>;
615
+ }, false>;
616
+
617
+ /** Maximum normalized package-catalog description length. */
618
+ export declare const MAX_CATALOG_DESCRIPTION_LENGTH = 500;
619
+
620
+ /** Maximum caller-controlled filesystem path depth accepted before filesystem work. */
621
+ export declare const MAX_FILESYSTEM_DEPTH = 64;
622
+
623
+ /** Maximum UTF-8 bytes parsed from one local package guide. */
624
+ export declare const MAX_GUIDE_BYTES = 5242880;
625
+
626
+ /** Maximum directory nesting accepted by one vendored-host traversal. */
627
+ export declare const MAX_HOST_DEPTH = 32;
628
+
629
+ /** Maximum filesystem entries accepted by one vendored-host traversal. */
630
+ export declare const MAX_HOST_ENTRIES = 4096;
631
+
632
+ /** Maximum UTF-8 bytes accepted in one portable filesystem path segment. */
633
+ export declare const MAX_PATH_SEGMENT_BYTES = 255;
634
+
635
+ /** Maximum caller-supplied endpoint characters inspected before URL allocation. */
636
+ export declare const MAX_SYNC_BASE_LENGTH = 2048;
637
+
638
+ /** Maximum portable Git branch characters accepted by the guide endpoint. */
639
+ export declare const MAX_SYNC_BRANCH_LENGTH = 255;
640
+
641
+ /** Maximum configurable cumulative response bytes retained by one Sync operation. */
642
+ export declare const MAX_SYNC_BUDGET = 104857600;
643
+
644
+ /** Maximum simultaneous upstream requests accepted by `Sync`. */
645
+ export declare const MAX_SYNC_CONCURRENCY = 64;
646
+
647
+ /** Maximum configurable dependencies or catalog entries for one Sync operation. */
648
+ export declare const MAX_SYNC_ITEMS = 1000;
649
+
650
+ /** Maximum response body accepted by `Sync`, in bytes. */
651
+ export declare const MAX_SYNC_LIMIT = 5242880;
652
+
653
+ /** Maximum retry count accepted by `Sync`. */
654
+ export declare const MAX_SYNC_RETRIES = 5;
655
+
656
+ /** Maximum per-request timeout accepted by `Sync`, in milliseconds. */
657
+ export declare const MAX_SYNC_TIMEOUT = 300000;
658
+
421
659
  /**
422
- * The `prune`-owned directories a hard allowlist; `pruneTargets` (and the
423
- * `Materializer.prune` that consumes it) never scans anything outside these two.
660
+ * Remove the canonical Orkestrel scope from a dependency name.
661
+ *
662
+ * @param name - A dependency name.
663
+ * @returns Its unscoped member when canonical, otherwise the original name.
664
+ */
665
+ export declare function packageShortName(name: string): string;
666
+
667
+ /** Parse a bounded dense array of host filesystem paths without invoking caller methods. */
668
+ export declare function parseFilesystemPaths(value: unknown, limit: number): readonly string[] | undefined;
669
+
670
+ /** Parse exact materializer options before allocating its emitter. */
671
+ export declare function parseMaterializerOptions(value: unknown): MaterializerOptions;
672
+
673
+ /** Parse a bounded dense array of portable paths without invoking caller methods. */
674
+ export declare function parsePortablePaths(value: unknown, limit: number): readonly string[] | undefined;
675
+
676
+ /** Parse and normalize one upstream HTTP(S) endpoint base. */
677
+ export declare function parseSyncBase(value: unknown): SyncBase;
678
+
679
+ /** Parse a safe Git branch path for the canonical raw-guide URL. */
680
+ export declare function parseSyncBranch(value: unknown): SyncBranch;
681
+
682
+ /**
683
+ * Snapshot only declared guide references while enforcing per-file and cumulative byte limits.
684
+ *
685
+ * @param value - The caller-supplied guide content map.
686
+ * @param names - Exact dependency names eligible for selection.
687
+ * @param budget - Maximum cumulative UTF-8 bytes.
688
+ * @returns A frozen selected map, or `undefined` when no map was supplied.
689
+ */
690
+ export declare function parseSyncCurrent(value: Readonly<Record<string, string>> | undefined, names: readonly string[], budget: number): Readonly<Record<string, string>> | undefined;
691
+
692
+ /** Parse and semantically validate dependency data before Sync performs network I/O. */
693
+ export declare function parseSyncDependencies(value: unknown, external: boolean): readonly Dependency[];
694
+
695
+ /**
696
+ * Parse the exact bounded `SyncOptions` boundary before allocating resources.
697
+ *
698
+ * @param value - Caller-supplied options.
699
+ * @returns A fresh, validated options record.
700
+ */
701
+ export declare function parseSyncOptions(value: unknown): SyncOptions;
702
+
703
+ /** Parse bounded exact transaction preconditions without invoking caller methods. */
704
+ export declare function parseWritePreconditions(value: unknown, limit: number): readonly WritePrecondition[] | undefined;
705
+
706
+ /**
707
+ * The prune-owned directories. Files outside these roots are never candidates
708
+ * for removal, including project-owned skills under `.agents/skills` and
709
+ * `.claude/skills`.
424
710
  *
425
711
  * @example
426
712
  * ```ts
427
713
  * import { PRUNE_DIRECTORIES } from '@orkestrel/scaffold/server'
428
714
  *
429
- * PRUNE_DIRECTORIES // ['.claude/agents', 'scripts']
715
+ * PRUNE_DIRECTORIES // ['.claude/agents', '.codex/agents', 'scripts']
430
716
  * ```
431
717
  */
432
- export declare const PRUNE_DIRECTORIES: readonly [".claude/agents", "scripts"];
718
+ export declare const PRUNE_DIRECTORIES: readonly string[];
433
719
 
434
720
  /**
435
721
  * List the repo-relative POSIX paths under `target`'s prune directories
436
- * (`.claude/agents`, `scripts`) that the vendored `host` allowlist does NOT
437
- * declare — THE single source of truth for prune drift, consumed by both
722
+ * (`.claude/agents`, `.codex/agents`, `scripts`) that the vendored `host`
723
+ * allowlist does NOT declare — THE single source of truth for prune drift, consumed by both
438
724
  * `Materializer.prune` (which deletes exactly these paths) and the bin's
439
725
  * audit/preview UX (which now shows them honestly instead of a
440
726
  * structurally-always-zero `audit.foreign`).
@@ -457,24 +743,56 @@ export declare const PRUNE_DIRECTORIES: readonly [".claude/agents", "scripts"];
457
743
  */
458
744
  export declare function pruneTargets(target: string, host: string): readonly string[];
459
745
 
746
+ /**
747
+ * Read one contained file as exact lowercase hexadecimal bytes.
748
+ *
749
+ * @param root - The declared containing root.
750
+ * @param path - The root-relative file path.
751
+ * @param code - The coded failure for containment or reading.
752
+ * @param boundary - The boundary name used in diagnostics.
753
+ * @returns The exact file bytes encoded as lowercase hexadecimal.
754
+ */
755
+ export declare function readFileHex(root: string, path: string, code: ScaffoldErrorCode, boundary: string, limit?: number): string;
756
+
757
+ /**
758
+ * Read one contained physical file as bounded UTF-8 text.
759
+ *
760
+ * @param root - The declared containing root.
761
+ * @param path - The root-relative file path.
762
+ * @param code - The coded failure for containment or reading.
763
+ * @param boundary - The boundary name used in diagnostics.
764
+ * @returns The exact file bytes decoded as UTF-8 text.
765
+ */
766
+ export declare function readFileText(root: string, path: string, code: ScaffoldErrorCode, boundary: string, limit?: number): string;
767
+
768
+ /**
769
+ * Read bounded physical local guide mirrors for declared dependencies.
770
+ *
771
+ * @param target - The package root.
772
+ * @param dependencies - The declared dependencies whose mirrors are eligible.
773
+ * @returns Existing guide content keyed by dependency name.
774
+ */
775
+ export declare function readGuideReferences(target: string, dependencies: readonly Dependency[]): Readonly<Record<string, string>>;
776
+
460
777
  /**
461
778
  * Read and validate a vendored host root's `manifest.json`, when present.
462
779
  *
463
780
  * @param host - The host root to probe.
464
- * @returns The parsed entries, or `undefined` when `host` has no
781
+ * @returns The parsed complete manifest, or `undefined` when `host` has no
465
782
  * `manifest.json` — the raw-repo-root fallback (`Materializer` then maps
466
783
  * an artifact's `source` to `host` 1:1, no vendored staging indirection).
467
784
  * @throws `ScaffoldError('TARGET', …)` when `manifest.json` exists but is
468
- * unreadable, is not valid JSON, or is not an array of `ManifestEntry`.
785
+ * unreadable, malformed, collision-prone, root-incomplete, or does not map
786
+ * bijectively and case-exactly onto real contained storage files.
469
787
  *
470
788
  * @example
471
789
  * ```ts
472
790
  * import { readHostManifest } from '@orkestrel/scaffold/server'
473
791
  *
474
- * readHostManifest('./dist/host') // readonly ManifestEntry[] | undefined
792
+ * readHostManifest('./dist/host') // HostManifest | undefined
475
793
  * ```
476
794
  */
477
- export declare function readHostManifest(host: string): readonly ManifestEntry[] | undefined;
795
+ export declare function readHostManifest(host: string): HostManifest | undefined;
478
796
 
479
797
  /**
480
798
  * Read `target/package.json` text — the read that feeds `manifestToDependencies`.
@@ -495,15 +813,14 @@ export declare function readHostManifest(host: string): readonly ManifestEntry[]
495
813
  export declare function readManifest(target: string): string;
496
814
 
497
815
  /**
498
- * Read a target's current content at a set of relative paths into a
499
- * `Record<string, string>` — the I/O that feeds the pure `diffPlan`.
816
+ * Read a target's current bytes at a set of relative paths into a
817
+ * byte-exact hexadecimal {@link Snapshot} — the I/O that feeds `diffPlan`.
500
818
  *
501
819
  * @param target - The target directory to read from.
502
820
  * @param paths - The plan-relative artifact paths to probe.
503
- * @returns A record keyed by path; a directory entry maps to `''` (presence
504
- * only a `host`-origin directory artifact is audited by presence, never
505
- * content), an absent path is OMITTED entirely (never an empty-string
506
- * placeholder for a missing file, so `diffPlan` reports it `missing`).
821
+ * @returns A snapshot keyed by path; each file maps to its exact lowercase
822
+ * hexadecimal bytes and a directly requested directory maps to `''`
823
+ * (presence only). An absent path is omitted entirely.
507
824
  * @throws `ScaffoldError('TARGET', …)` when an EXISTING path fails to read
508
825
  * (e.g. `EACCES` / `EPERM`) — carries the offending relative `path` (and
509
826
  * the resolved `full` path) in `context`. An absent path is never an
@@ -514,10 +831,88 @@ export declare function readManifest(target: string): string;
514
831
  * import { readTarget } from '@orkestrel/scaffold/server'
515
832
  *
516
833
  * readTarget('./packages/router', ['package.json', 'src/core/index.ts'])
517
- * // { 'package.json': '{ "name": }', 'src/core/index.ts': '…' }
834
+ * // { 'package.json': '7b226e616d65223a…', 'src/core/index.ts': '6578706f7274…' }
518
835
  * ```
519
836
  */
520
- export declare function readTarget(target: string, paths: readonly string[]): Readonly<Record<string, string>>;
837
+ export declare function readTarget(target: string, paths: readonly string[]): Snapshot;
838
+
839
+ /**
840
+ * Map a manifest destination from an artifact's source prefix to its target prefix.
841
+ *
842
+ * @param artifact - The host artifact carrying the target path and optional source.
843
+ * @param destination - The matched manifest destination.
844
+ * @returns The exact target-relative path for the matched manifest file.
845
+ * @throws `ScaffoldError('INVALID', …)` when `destination` is outside the source prefix.
846
+ */
847
+ export declare function remapArtifactPath(artifact: HostArtifact, destination: string): string;
848
+
849
+ /**
850
+ * Atomically promote a staged sibling directory while preserving recoverable state.
851
+ *
852
+ * @param staging - The completed staging directory.
853
+ * @param target - The destination directory to replace.
854
+ * @param backup - The sibling path reserved for the prior target.
855
+ * @throws `ScaffoldError('WRITE', …)` with explicit `committed` and recovery paths.
856
+ */
857
+ export declare function replaceDirectory(staging: string, target: string, backup: string): void;
858
+
859
+ /** Windows device names that remain reserved even when followed by an extension. */
860
+ export declare const RESERVED_PATH_SEGMENT_PATTERN: RegExp;
861
+
862
+ /** Existing repository metadata that a materialization target must never own. */
863
+ export declare const RESERVED_TARGET_PATH_PATTERN: RegExp;
864
+
865
+ /**
866
+ * Resolve a path beneath a declared root and reject lexical or symlink escape.
867
+ *
868
+ * @param root - The containing filesystem root.
869
+ * @param path - The candidate path, relative or absolute.
870
+ * @param code - The coded error to raise on escape.
871
+ * @param boundary - The boundary name used in diagnostics.
872
+ * @returns The lexically resolved candidate after realpath-aware validation.
873
+ */
874
+ export declare function resolveContainedPath(root: string, path: string, code: ScaffoldErrorCode, boundary: string): string;
875
+
876
+ /**
877
+ * Resolve and completely preflight the canonical guide destinations a sync may write.
878
+ *
879
+ * @param guides - Structurally validated behind guide results.
880
+ * @param target - The repository root that owns `guides/src`.
881
+ * @returns Each guide paired with its contained destination.
882
+ * @throws `ScaffoldError('WRITE', ...)` before mutation for ownership, collision,
883
+ * containment, or existing filesystem-shape violations.
884
+ */
885
+ export declare function resolveGuideWrites(guides: readonly GuideSync[], target: string): readonly GuideWrite[];
886
+
887
+ /**
888
+ * Resolve a contained path whose existing ancestor chain contains no links.
889
+ *
890
+ * @param root - The trusted lexical and physical root.
891
+ * @param path - The portable root-relative path.
892
+ * @param code - The coded error to raise on failure.
893
+ * @param boundary - The boundary name used in diagnostics.
894
+ * @returns The contained destination.
895
+ * @throws `ScaffoldError` when containment fails or an ancestor is not a real directory.
896
+ */
897
+ export declare function resolvePhysicalPath(root: string, path: string, code: ScaffoldErrorCode, boundary: string): string;
898
+
899
+ /**
900
+ * Resolve the deepest existing ancestor of a path through the real filesystem.
901
+ *
902
+ * @param path - The absolute or relative path to resolve.
903
+ * @returns A path whose existing prefix has been resolved through symlinks.
904
+ */
905
+ export declare function resolveRealPath(path: string): string;
906
+
907
+ /**
908
+ * Restore quarantined files to their original target-relative paths.
909
+ *
910
+ * @param transaction - The nominal transaction that owns the quarantine and target.
911
+ * @param paths - The relative paths to restore, in their original move order.
912
+ * @throws `ScaffoldError('WRITE', …)` after attempting every reverse-order
913
+ * restoration when one or more files could not be restored.
914
+ */
915
+ export declare function restoreFiles(transaction: WriteTransaction, paths: readonly string[]): void;
521
916
 
522
917
  /**
523
918
  * Filter a manifest record's entries down to `@orkestrel/`-prefixed keys with
@@ -539,6 +934,9 @@ export declare function readTarget(target: string, paths: readonly string[]): Re
539
934
  */
540
935
  export declare function selectOrkestrelEntries(value: unknown): readonly (readonly [string, string])[];
541
936
 
937
+ /** Local configuration and credential-like paths that must never enter a vendored host. */
938
+ export declare const SENSITIVE_HOST_PATH_PATTERN: RegExp;
939
+
542
940
  /**
543
941
  * Stage the vendored host set (byte-preserved copies + `manifest.json`) from
544
942
  * a repo root into an output directory — the BUILD-time primitive the
@@ -547,26 +945,20 @@ export declare function selectOrkestrelEntries(value: unknown): readonly (readon
547
945
  * writes (via `hostRoot` / `readHostManifest`).
548
946
  *
549
947
  * @param root - The repo root every `paths` entry resolves against.
550
- * @param out - The output directory to wipe and stage into (typically `dist/host`).
948
+ * @param out - The output directory to replace after staging completes.
551
949
  * @param paths - The repo-relative file/directory entries to stage; defaults
552
950
  * to the package's own vendored set (`HOST_PATHS`) — a caller passes an
553
951
  * explicit list only to stage an arbitrary/test set.
554
952
  * @remarks
555
- * `out` is wiped (`rmSync(out, { recursive: true, force: true })`) BEFORE
556
- * staging, so a stale file left over from a prior run never lingers. Each
557
- * `paths` entry is walked to its per-file leaves (a directory recursively,
558
- * via `listFiles`; a file, itself) and copied byte-for-byte
559
- * (`copyFileSync`) to `<out>/<storagePath(path)>`. The `executable` flag
560
- * `entries` reports is derived from the `.sh` suffix on `destination` —
561
- * deterministic on every build platform (Windows `stat` carries no execute
562
- * bit); all vendored executables are shell scripts by construction.
563
- * `manifest.json` is written LAST, as `entries` code-unit sorted by
564
- * `destination`, tab-indented JSON with a trailing newline.
953
+ * Every source, path collision, and root/output relationship is preflighted
954
+ * before output mutation. Files are copied into a temporary sibling; the
955
+ * completed staging tree atomically replaces `out`, with rollback when the
956
+ * swap fails. The manifest records both sorted file `entries` and the
957
+ * complete sorted directory `roots` inventory so destructive consumers can
958
+ * distinguish a declared-empty root from a truncated manifest.
565
959
  * @returns The written manifest's entries (`{ storage, destination, executable }`).
566
- * @throws `ScaffoldError('TARGET', …)` naming the offending path when a
567
- * `paths` entry has no source under `root`, or naming BOTH colliding
568
- * destinations when two entries map to the same `storagePath` (the guard
569
- * run BEFORE `manifest.json` is written).
960
+ * @throws `ScaffoldError('TARGET', …)` for an invalid/escaping source or path
961
+ * collision, and `ScaffoldError('WRITE', …)` for staging/swap failures.
570
962
  *
571
963
  * @example
572
964
  * ```ts
@@ -582,7 +974,7 @@ export declare function stageHost(root: string, out: string, paths?: readonly st
582
974
  * Map a repo-relative path to its vendored-host STAGING path, per the
583
975
  * dotfile-mapping rule `stageHost` writes into `manifest.json`.
584
976
  *
585
- * @param path - The repo-relative source path (e.g. `.claude/agents/scout.md`).
977
+ * @param path - The repo-relative source path (e.g. `.claude/agents/reviewer.md`).
586
978
  * @returns The mapped storage path: a leading-dot TOP-LEVEL FILE maps to
587
979
  * `dotfiles/<name-without-dot>`; a leading-dot DIRECTORY segment loses its
588
980
  * dot wherever it appears; an undotted path is unchanged.
@@ -592,7 +984,7 @@ export declare function stageHost(root: string, out: string, paths?: readonly st
592
984
  * import { storagePath } from '@orkestrel/scaffold/server'
593
985
  *
594
986
  * storagePath('.gitignore') // 'dotfiles/gitignore'
595
- * storagePath('.claude/agents/scout.md') // 'claude/agents/scout.md'
987
+ * storagePath('.claude/agents/reviewer.md') // 'claude/agents/reviewer.md'
596
988
  * storagePath('.github/workflows/ci.yml') // 'github/workflows/ci.yml'
597
989
  * storagePath('AGENTS.md') // 'AGENTS.md'
598
990
  * ```
@@ -665,13 +1057,25 @@ export declare class Sync implements SyncInterface {
665
1057
  * alongside the existing `error` events for each degraded sub-fetch.
666
1058
  */
667
1059
  catalog(): Promise<readonly CatalogEntry[]>;
668
- pull(target: string): Promise<SyncReport>;
1060
+ pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
669
1061
  write(report: SyncReport, target: string): Promise<readonly string[]>;
670
1062
  destroy(): void;
671
1063
  }
672
1064
 
1065
+ /** Safe ASCII branch alphabet accepted before Git ref-component validation. */
1066
+ export declare const SYNC_BRANCH_PATTERN: RegExp;
1067
+
1068
+ /** Mutable one-cell byte allowance shared by concurrent Sync readers. */
1069
+ export declare type SyncAllowance = Float64Array;
1070
+
1071
+ /** One normalized upstream HTTP(S) endpoint base accepted by `Sync`. */
1072
+ export declare type SyncBase = string;
1073
+
1074
+ /** One bounded Git-compatible branch path accepted by the guide endpoint. */
1075
+ export declare type SyncBranch = string;
1076
+
673
1077
  /**
674
- * `Sync`'s push observation surface (AGENTS §13, server).
1078
+ * `Sync`'s push observation channel (AGENTS §13, server).
675
1079
  *
676
1080
  * @remarks
677
1081
  * `package` fires once per `catalog()` entry processed; its `note` is the
@@ -691,6 +1095,13 @@ export declare type SyncEventMap = {
691
1095
  readonly destroy: readonly [];
692
1096
  };
693
1097
 
1098
+ /** Build the closed upstream-guide endpoint options shape. */
1099
+ export declare function syncGuideOptionsShape(): ObjectShape<{
1100
+ base: OptionalShape<StringShape>;
1101
+ branch: OptionalShape<StringShape>;
1102
+ timeout: OptionalShape<NumberShape>;
1103
+ }, false>;
1104
+
694
1105
  /**
695
1106
  * The upstream-synchronization contract (server) — the impure FETCH sibling
696
1107
  * of `MaterializerInterface`.
@@ -700,7 +1111,7 @@ export declare interface SyncInterface {
700
1111
  guides(deps: readonly Dependency[], current?: Readonly<Record<string, string>>): Promise<readonly GuideSync[]>;
701
1112
  versions(deps: readonly Dependency[]): Promise<readonly VersionSync[]>;
702
1113
  catalog(): Promise<readonly CatalogEntry[]>;
703
- pull(target: string): Promise<SyncReport>;
1114
+ pull(target: string, dependencies?: readonly Dependency[]): Promise<SyncReport>;
704
1115
  write(report: SyncReport, target: string): Promise<readonly string[]>;
705
1116
  destroy(): void;
706
1117
  }
@@ -730,22 +1141,104 @@ export declare interface SyncInterface {
730
1141
  */
731
1142
  export declare interface SyncOptions {
732
1143
  readonly guides?: {
733
- readonly base?: string;
734
- readonly branch?: string;
1144
+ readonly base?: SyncBase;
1145
+ readonly branch?: SyncBranch;
735
1146
  readonly timeout?: number;
736
1147
  };
737
1148
  readonly registry?: {
738
- readonly base?: string;
1149
+ readonly base?: SyncBase;
739
1150
  readonly timeout?: number;
740
1151
  };
741
1152
  readonly concurrency?: number;
742
1153
  readonly retries?: number;
743
1154
  readonly strict?: boolean;
744
1155
  readonly limit?: number;
1156
+ readonly items?: number;
1157
+ readonly budget?: number;
745
1158
  readonly on?: EmitterHooks<SyncEventMap>;
746
1159
  readonly error?: EmitterErrorHandler;
747
1160
  }
748
1161
 
1162
+ /** The compiled, closed data-only `SyncOptions` contract. */
1163
+ export declare const syncOptionsContract: ContractInterface<Readonly<{} & {
1164
+ guides?: Readonly<{} & {
1165
+ base?: string;
1166
+ branch?: string;
1167
+ timeout?: number;
1168
+ }>;
1169
+ registry?: Readonly<{} & {
1170
+ base?: string;
1171
+ timeout?: number;
1172
+ }>;
1173
+ concurrency?: number;
1174
+ retries?: number;
1175
+ strict?: boolean;
1176
+ limit?: number;
1177
+ items?: number;
1178
+ budget?: number;
1179
+ }>>;
1180
+
1181
+ /** Build the closed data-only `SyncOptions` shape. */
1182
+ export declare function syncOptionsShape(): ObjectShape<{
1183
+ guides: OptionalShape<ObjectShape<{
1184
+ base: OptionalShape<StringShape>;
1185
+ branch: OptionalShape<StringShape>;
1186
+ timeout: OptionalShape<NumberShape>;
1187
+ }, false>>;
1188
+ registry: OptionalShape<ObjectShape<{
1189
+ base: OptionalShape<StringShape>;
1190
+ timeout: OptionalShape<NumberShape>;
1191
+ }, false>>;
1192
+ concurrency: OptionalShape<NumberShape>;
1193
+ retries: OptionalShape<NumberShape>;
1194
+ strict: OptionalShape<BooleanShape>;
1195
+ limit: OptionalShape<NumberShape>;
1196
+ items: OptionalShape<NumberShape>;
1197
+ budget: OptionalShape<NumberShape>;
1198
+ }, false>;
1199
+
1200
+ /** Build the closed registry endpoint options shape. */
1201
+ export declare function syncRegistryOptionsShape(): ObjectShape<{
1202
+ base: OptionalShape<StringShape>;
1203
+ timeout: OptionalShape<NumberShape>;
1204
+ }, false>;
1205
+
1206
+ /**
1207
+ * Assemble one synchronization report from already ordered guide and version outcomes.
1208
+ *
1209
+ * @param target - The target package root.
1210
+ * @param guides - Guide outcomes.
1211
+ * @param versions - Version outcomes.
1212
+ * @returns The derived whole report.
1213
+ */
1214
+ export declare function syncReportOf(target: string, guides: readonly GuideSync[], versions: readonly VersionSync[]): SyncReport;
1215
+
1216
+ /**
1217
+ * Revalidate one captured physical-directory identity without following links.
1218
+ *
1219
+ * @param anchor - The captured path, device, and inode.
1220
+ * @param boundary - The boundary name used in diagnostics.
1221
+ * @throws `ScaffoldError('WRITE', ...)` when the directory is missing or changed.
1222
+ */
1223
+ export declare function validateWriteAnchor(anchor: WriteAnchor, boundary: string): void;
1224
+
1225
+ /**
1226
+ * Revalidate every private directory owned by a write transaction.
1227
+ *
1228
+ * @param transaction - The nominal transaction state.
1229
+ * @throws `ScaffoldError('WRITE', ...)` when any private directory changed.
1230
+ */
1231
+ export declare function validateWriteDirectories(transaction: WriteTransaction): void;
1232
+
1233
+ /**
1234
+ * Revalidate the transaction target against its original or transaction-owned identity.
1235
+ *
1236
+ * @param transaction - The nominal transaction state.
1237
+ * @param owned - A target directory created by the current commit.
1238
+ * @throws `ScaffoldError('WRITE', ...)` when the target identity changed.
1239
+ */
1240
+ export declare function validateWriteTarget(transaction: WriteTransaction, owned: WriteAnchor | undefined): void;
1241
+
749
1242
  /**
750
1243
  * The vendored set of destination-relative paths under `directory` (one of
751
1244
  * `PRUNE_DIRECTORIES`) that `pruneTargets` must NOT report — read from the
@@ -771,9 +1264,64 @@ export declare interface SyncOptions {
771
1264
  * ```ts
772
1265
  * import { vendoredPruneSet } from '@orkestrel/scaffold/server'
773
1266
  *
774
- * vendoredPruneSet('./dist/host', '.claude/agents') // Set { '.claude/agents/scout.md', … }
1267
+ * vendoredPruneSet('./dist/host', '.claude/agents') // Set { '.claude/agents/reviewer.md', … }
775
1268
  * ```
776
1269
  */
777
1270
  export declare function vendoredPruneSet(host: string, directory: string): ReadonlySet<string>;
778
1271
 
1272
+ /** Exact SHA-256 digest accepted by write preconditions. */
1273
+ export declare const WRITE_DIGEST_PATTERN: RegExp;
1274
+
1275
+ /** A physical directory identity captured across a write transaction. */
1276
+ export declare interface WriteAnchor {
1277
+ readonly path: string;
1278
+ readonly device: number;
1279
+ readonly inode: number;
1280
+ }
1281
+
1282
+ /** Anchored result of creating a physical directory path one segment at a time. */
1283
+ export declare interface WriteDirectoryResult {
1284
+ readonly anchor: WriteAnchor;
1285
+ readonly created: readonly WriteAnchor[];
1286
+ }
1287
+
1288
+ /** One destination snapshot required to remain stable through a write commit. */
1289
+ export declare interface WriteExpectation {
1290
+ readonly path: string;
1291
+ readonly shape: 'absent' | 'file' | 'directory';
1292
+ readonly device?: number;
1293
+ readonly inode?: number;
1294
+ readonly modified?: number;
1295
+ readonly size?: number;
1296
+ readonly digest?: string;
1297
+ }
1298
+
1299
+ /** Caller-observed destination state that a write transaction must still match. */
1300
+ export declare interface WritePrecondition {
1301
+ readonly path: string;
1302
+ readonly shape: 'absent' | 'file';
1303
+ readonly digest?: string;
1304
+ }
1305
+
1306
+ /**
1307
+ * Nominal, same-volume write-transaction state. Construction derives every
1308
+ * filesystem path from a target and portable relative paths; callers cannot
1309
+ * supply a deletion root or mutate captured arrays.
1310
+ */
1311
+ export declare class WriteTransaction {
1312
+ #private;
1313
+ constructor(token: unknown, target: string, root: string, stage: string, backup: string, expectations: readonly WriteExpectation[], parents: readonly WriteAnchor[], directories: readonly WriteAnchor[], anchor: WriteAnchor, existing: WriteAnchor | undefined);
1314
+ /** Create private staging state and snapshot every destination before mutation. */
1315
+ static create(target: string, paths: readonly string[], preconditions?: readonly WritePrecondition[]): WriteTransaction;
1316
+ get target(): string;
1317
+ get root(): string;
1318
+ get stage(): string;
1319
+ get backup(): string;
1320
+ get expectations(): readonly WriteExpectation[];
1321
+ get parents(): readonly WriteAnchor[];
1322
+ get directories(): readonly WriteAnchor[];
1323
+ get anchor(): WriteAnchor;
1324
+ get existing(): WriteAnchor | undefined;
1325
+ }
1326
+
779
1327
  export { }