@mnstry/atelier 0.2.0-alpha.13 → 0.2.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +283 -0
- package/README.md +102 -52
- package/SECURITY.md +1 -1
- package/TRADEMARKS.md +2 -2
- package/contracts/atelier-extension-manifest.v1.schema.json +412 -0
- package/contracts/atelier-graph-view.v1.schema.json +1085 -0
- package/contracts/public-api-baseline.json +298 -19
- package/docs/README.md +90 -0
- package/docs/blocks/capability-stewardship.md +1 -1
- package/docs/blocks/claims.md +2 -2
- package/docs/blocks/conformance-admission.md +1 -1
- package/docs/blocks/learning-harnesses.md +1 -1
- package/docs/{atelier-sync.md → guides/atelier-sync.md} +20 -14
- package/docs/{capability-stewardship.md → guides/capability-stewardship.md} +2 -4
- package/docs/guides/co-authoring.md +113 -0
- package/docs/{distributions.md → guides/distributions.md} +13 -8
- package/docs/guides/extensions.md +189 -0
- package/docs/guides/graph-refresh.md +152 -0
- package/docs/guides/graph-workspace.md +64 -0
- package/docs/{ingestion.md → guides/ingestion.md} +16 -15
- package/docs/{inquiry-harnesses.md → guides/inquiry-harnesses.md} +2 -3
- package/docs/{knowledge-workspace.md → guides/knowledge-workspace.md} +2 -2
- package/docs/{learning-harnesses.md → guides/learning-harnesses.md} +2 -3
- package/docs/{learning.md → guides/learning.md} +12 -5
- package/docs/{obsidian.md → guides/obsidian.md} +370 -23
- package/docs/guides/ontology-guide.md +111 -0
- package/docs/{personal-workspace.md → guides/personal-workspace.md} +17 -5
- package/docs/{research-starter.md → guides/research-starter.md} +1 -1
- package/docs/{tenant-readiness.md → guides/tenant-readiness.md} +39 -0
- package/docs/{upgrade.md → guides/upgrade.md} +197 -11
- package/docs/knowledge-health/assessment/capabilities.md +6 -15
- package/docs/knowledge-health/facilitator.md +15 -4
- package/docs/knowledge-health/{devday/participant-response.md → participant-response.md} +4 -2
- package/docs/knowledge-health/quickstart.md +97 -75
- package/docs/knowledge-health/readme.md +13 -5
- package/docs/maintainers/describing-the-atelier.md +173 -0
- package/docs/{release-engineering.md → maintainers/release-engineering.md} +5 -4
- package/docs/{announcements.md → reference/announcements.md} +1 -1
- package/docs/{architecture.md → reference/architecture.md} +31 -5
- package/docs/{atelier-runtime.md → reference/atelier-runtime.md} +1 -1
- package/docs/{connected-composition.md → reference/connected-composition.md} +7 -10
- package/docs/{coordination.md → reference/coordination.md} +1 -1
- package/docs/{decisions.md → reference/decisions.md} +3 -3
- package/docs/{design.md → reference/design.md} +1 -1
- package/docs/reference/edge-policy.md +151 -0
- package/docs/reference/graph-view-structured.md +119 -0
- package/docs/reference/graph-view.md +139 -0
- package/docs/reference/graph-workspace.md +37 -0
- package/docs/{intake-and-guides.md → reference/intake-and-guides.md} +3 -4
- package/docs/{knowledge-graph.md → reference/knowledge-graph.md} +120 -6
- package/docs/{local-services.md → reference/local-services.md} +43 -4
- package/docs/{obsidian-contract.md → reference/obsidian-contract.md} +29 -4
- package/docs/{obsidian-plugin.md → reference/obsidian-plugin.md} +218 -68
- package/docs/{practical-judgment.md → reference/phronesis.md} +12 -3
- package/docs/reference/portability.md +92 -0
- package/docs/{portable-presentation.md → reference/portable-presentation.md} +2 -2
- package/docs/{presentation-consumer-boundaries.md → reference/presentation-consumer-boundaries.md} +5 -0
- package/docs/{project-options.md → reference/project-options.md} +17 -0
- package/docs/{repo-boundary-guard.md → reference/repo-boundary-guard.md} +50 -4
- package/docs/{review-portability.md → reference/review-portability.md} +4 -0
- package/docs/{semantic-operation.md → reference/semantic-operation.md} +4 -3
- package/docs/{template-profile.md → reference/template-profile.md} +1 -1
- package/docs/{trackables.md → reference/trackables.md} +2 -2
- package/docs/start/independent-start.md +89 -0
- package/docs/{install.md → start/install.md} +9 -9
- package/docs/{knowledge-setup.md → start/knowledge-setup.md} +5 -5
- package/docs/{research-starter-quickstart.md → start/research-starter-quickstart.md} +6 -6
- package/fixtures/atelier-architecture/catalog/invalid/document.json +2 -2
- package/fixtures/atelier-architecture/catalog/valid/document.json +2 -2
- package/fixtures/graph-view/synthetic-authorized-input.v1.json +437 -0
- package/fixtures/graph-view/synthetic-view.v1.json +1 -0
- package/fixtures/knowledge-health/workshop/README.md +1 -1
- package/fixtures/templates/local-library.v1.json +3 -1
- package/package.json +22 -8
- package/plugins/obsidian/main.js +57 -8
- package/plugins/obsidian/manifest.json +1 -1
- package/skills/claude/atelier-guided-upgrade/SKILL.md +1 -1
- package/skills/claude/atelier-knowledge-setup/SKILL.md +2 -2
- package/skills/claude/atelier-local-service/SKILL.md +1 -1
- package/skills/claude/atelier-practical-judgment/SKILL.md +7 -2
- package/skills/claude/atelier-skill-steward/SKILL.md +1 -1
- package/skills/claude/mnstry-readiness/SKILL.md +2 -2
- package/skills/codex/atelier-guided-upgrade/SKILL.md +1 -1
- package/skills/codex/atelier-knowledge-setup/SKILL.md +2 -2
- package/skills/codex/atelier-local-service/SKILL.md +1 -1
- package/skills/codex/atelier-practical-judgment/SKILL.md +7 -2
- package/skills/codex/atelier-skill-steward/SKILL.md +1 -1
- package/skills/codex/mnstry-readiness/SKILL.md +2 -2
- package/src/architecture/catalog.mjs +2 -2
- package/src/attestation/sign.mjs +1 -1
- package/src/boundary/content-rules.mjs +534 -68
- package/src/boundary/policy.mjs +187 -60
- package/src/cli/run.mjs +26 -9
- package/src/commands/distribution.mjs +1 -1
- package/src/commands/extension.mjs +27 -0
- package/src/commands/ingest.mjs +1 -1
- package/src/commands/learn.mjs +1 -1
- package/src/commands/obsidian.mjs +49 -21
- package/src/commands/review.mjs +3 -0
- package/src/edge-policy/contract.mjs +69 -0
- package/src/edge-policy/policy.mjs +142 -0
- package/src/edge-policy/source-citation-adapter.mjs +43 -0
- package/src/extension-packs/loader.mjs +4 -3
- package/src/extensions/admission.mjs +83 -0
- package/src/extensions/client.d.mts +23 -0
- package/src/extensions/client.mjs +99 -0
- package/src/extensions/contracts.d.mts +29 -0
- package/src/extensions/contracts.mjs +163 -0
- package/src/extensions/convex.d.mts +24 -0
- package/src/extensions/convex.mjs +189 -0
- package/src/extensions/delegation.mjs +54 -0
- package/src/extensions/drizzle.d.mts +11 -0
- package/src/extensions/drizzle.mjs +33 -0
- package/src/extensions/postgres.d.mts +7 -0
- package/src/extensions/postgres.mjs +66 -0
- package/src/extensions/prisma.d.mts +13 -0
- package/src/extensions/prisma.mjs +63 -0
- package/src/extensions/project.mjs +432 -0
- package/src/extensions/scaffold-convex.mjs +130 -0
- package/src/extensions/scaffold-sql.mjs +144 -0
- package/src/extensions/server.d.mts +20 -0
- package/src/extensions/server.mjs +102 -0
- package/src/extensions/sql-scope.mjs +22 -0
- package/src/graph/graph.mjs +13 -5
- package/src/graph/knowledge-graph.mjs +20 -2
- package/src/graph/repo-contribution.mjs +405 -0
- package/src/graph/repo-contribution.types.mjs +32 -0
- package/src/graph-access/edge-decision-adapter.mjs +41 -0
- package/src/graph-access/edge-record-adapter.mjs +73 -0
- package/src/graph-access/index.mjs +253 -0
- package/src/graph-access/multi-contribution-record-adapter.mjs +101 -0
- package/src/graph-access/multi-record-projection-adapter.mjs +74 -0
- package/src/graph-access/source-adapter.mjs +81 -0
- package/src/graph-refresh/compare.mjs +110 -0
- package/src/graph-refresh/controller.mjs +324 -0
- package/src/graph-refresh/index.mjs +3 -0
- package/src/graph-refresh/local.mjs +78 -0
- package/src/graph-view/babylon/babylon-scene-port.mjs +64 -0
- package/src/graph-view/babylon/index.mjs +2 -0
- package/src/graph-view/babylon/navigator.mjs +312 -0
- package/src/graph-view/babylon/provenance.mjs +36 -0
- package/src/graph-view/babylon/scene-controller.mjs +131 -0
- package/src/graph-view/babylon/semantic-picking.mjs +35 -0
- package/src/graph-view/babylon/web-host.mjs +188 -0
- package/src/graph-view/cli.mjs +3 -0
- package/src/graph-view/command.mjs +34 -0
- package/src/graph-view/contracts.mjs +79 -0
- package/src/graph-view/errors.mjs +5 -0
- package/src/graph-view/index.mjs +4 -0
- package/src/graph-view/intents.mjs +30 -0
- package/src/graph-view/query.mjs +213 -0
- package/src/graph-view/refs.mjs +2 -0
- package/src/graph-view/structured/browser.mjs +262 -0
- package/src/graph-view/structured/styles.mjs +8 -0
- package/src/graph-workspace/authoring.mjs +185 -0
- package/src/graph-workspace/browser/constellation.mjs +369 -0
- package/src/graph-workspace/browser/dom.mjs +23 -0
- package/src/graph-workspace/browser/index.mjs +6 -0
- package/src/graph-workspace/browser/review.mjs +55 -0
- package/src/graph-workspace/browser/styles.mjs +11 -0
- package/src/graph-workspace/browser/workbench.mjs +351 -0
- package/src/graph-workspace/browser/workspace.mjs +431 -0
- package/src/graph-workspace/disclosure.mjs +39 -0
- package/src/graph-workspace/geometry.mjs +78 -0
- package/src/graph-workspace/index.mjs +11 -0
- package/src/graph-workspace/labels.mjs +50 -0
- package/src/graph-workspace/model.mjs +109 -0
- package/src/graph-workspace/recipes.mjs +88 -0
- package/src/graph-workspace/scale.mjs +95 -0
- package/src/graph-workspace/theme.mjs +156 -0
- package/src/ingestion/optional/graphify/graphify_supplier.py +5 -0
- package/src/ingestion/optional/graphify/native-closure-pins.json +275 -0
- package/src/ingestion/optional/graphify/native-pins.json +306 -0
- package/src/knowledge/ontology-guide.mjs +152 -0
- package/src/knowledge-health/proof/installed-check.mjs +2 -2
- package/src/knowledge-health/rule-identity.mjs +14 -9
- package/src/knowledge-health/workshop.mjs +22 -2
- package/src/personal-workspace/index.mjs +1 -1
- package/src/personal-workspace/selection.mjs +34 -3
- package/src/project/config.mjs +1 -1
- package/src/project/path-match.mjs +2 -1
- package/src/project/providers/github.mjs +24 -0
- package/src/project/repo-identity.mjs +3 -22
- package/src/projection/obsidian/contracts.mjs +1 -1
- package/src/projection/obsidian/materialize/path-registry.mjs +1 -1
- package/src/projection/obsidian/materialize/prepare-view.mjs +88 -6
- package/src/projection/obsidian/materialize/redaction.mjs +1 -1
- package/src/projection/obsidian/personal-workspace.mjs +84 -5
- package/src/projection/obsidian/plugin-bridge/channel.mjs +1 -1
- package/src/projection/obsidian/proposals/adapter.mjs +46 -23
- package/src/projection/obsidian/proposals/observation.mjs +40 -2
- package/src/projection/obsidian/publication/publisher.mjs +96 -8
- package/src/projection/project.mjs +21 -16
- package/src/readiness/readiness.mjs +20 -11
- package/src/readiness-protocols/bundled-pack.mjs +14 -766
- package/src/readiness-protocols/bundled-selection.mjs +32 -0
- package/src/readiness-protocols/evidence.mjs +2 -1
- package/src/readiness-protocols/packs/mnstry.mjs +766 -0
- package/src/readiness-protocols/runtime.mjs +43 -6
- package/src/runtime/obsidian/engine.mjs +387 -76
- package/src/runtime/obsidian/lifecycle.mjs +20 -15
- package/src/runtime/obsidian/login-item.mjs +6 -3
- package/src/runtime/obsidian/machine-settings.mjs +52 -8
- package/src/runtime/obsidian/observation.mjs +36 -4
- package/src/runtime/obsidian/opening.mjs +4 -3
- package/src/runtime/obsidian/personal-selection.mjs +187 -0
- package/src/runtime/obsidian/pipeline.mjs +115 -6
- package/src/runtime/obsidian/private-lock.mjs +50 -6
- package/src/runtime/obsidian/selection-continuity.mjs +124 -0
- package/src/runtime/obsidian/service-main.mjs +68 -16
- package/src/runtime/obsidian/service.mjs +80 -6
- package/src/runtime/obsidian/unheld-evidence.mjs +1 -1
- package/src/runtime/obsidian/view-permission.mjs +335 -0
- package/src/ui/control-system.mjs +201 -0
- package/src/ui/html-primitives.mjs +3 -0
- package/src/upgrade/closure-diagnostics.mjs +135 -0
- package/src/upgrade/closure.mjs +649 -0
- package/src/upgrade/upgrade.mjs +42 -11
- package/templates/distribution-workspace/README.md +2 -2
- package/templates/external-project-workspace/atelier.project.json +3 -0
- package/templates/external-project-workspace/pack-lifecycle.json +3 -1
- package/templates/knowledge-workspace/README.md +2 -2
- package/docs/integration-and-review-plan.md +0 -409
- package/docs/integration-contract-decisions.md +0 -44
- package/docs/knowledge-health/devday/facilitator.md +0 -16
- package/docs/knowledge-health/devday/public-default.md +0 -107
- package/docs/knowledge-health/devday/quickstart.md +0 -18
- package/docs/knowledge-health/devday/support.md +0 -22
- package/docs/knowledge-health/exercise.md +0 -17
- package/docs/knowledge-health/support.md +0 -34
- package/docs/presentation-review-disposition.md +0 -47
- package/docs/progressive-ingestion-acceptance.md +0 -89
- package/docs/progressive-ingestion.md +0 -235
- /package/docs/{exact-upgrades.md → guides/exact-upgrades.md} +0 -0
- /package/docs/{guided-upgrades.md → guides/guided-upgrades.md} +0 -0
- /package/docs/{local-review.md → guides/local-review.md} +0 -0
- /package/docs/{skill-steward.md → guides/skill-steward.md} +0 -0
- /package/docs/{template-upgrades.md → guides/template-upgrades.md} +0 -0
- /package/docs/{assurance-controls.md → maintainers/assurance-controls.md} +0 -0
- /package/docs/{attestation.md → reference/attestation.md} +0 -0
- /package/docs/{coauthor-session.md → reference/coauthor-session.md} +0 -0
- /package/docs/{continuity.md → reference/continuity.md} +0 -0
- /package/docs/{contract-stability.md → reference/contract-stability.md} +0 -0
- /package/docs/{decision-practices.md → reference/decision-practices.md} +0 -0
- /package/docs/{evidence-navigation.md → reference/evidence-navigation.md} +0 -0
- /package/docs/{extension-pack-lifecycle.md → reference/extension-pack-lifecycle.md} +0 -0
- /package/docs/{interaction-and-reflection.md → reference/interaction-and-reflection.md} +0 -0
- /package/docs/{local-state.md → reference/local-state.md} +0 -0
- /package/docs/{ontology.md → reference/ontology.md} +0 -0
- /package/docs/{optional-ingestion.md → reference/optional-ingestion.md} +0 -0
- /package/docs/{project-command-inventory.md → reference/project-command-inventory.md} +0 -0
- /package/docs/{semantic-ingestion.md → reference/semantic-ingestion.md} +0 -0
- /package/docs/{vault-service.md → reference/vault-service.md} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCooperativeBudget, isSchedulingStop } from '../materialize/prepare-view.mjs'
|
|
1
2
|
import fs from 'node:fs'
|
|
2
3
|
import fsp from 'node:fs/promises'
|
|
3
4
|
import path from 'node:path'
|
|
@@ -15,6 +16,10 @@ import { probeExchange } from './exchange.mjs'
|
|
|
15
16
|
import { CRASH_INJECTION_TEST_SEAM } from './test-seam.mjs'
|
|
16
17
|
import { createDirectAdapter } from './transport.mjs'
|
|
17
18
|
|
|
19
|
+
// The caller's private-state location refusals: once the state root may lie in a declared repository, no further
|
|
20
|
+
// entry is written there.
|
|
21
|
+
const isLocationRefusal = (error) => /^managed-root-|^repository-inside-managed-root$/.test(String(error?.code ?? ''))
|
|
22
|
+
|
|
18
23
|
// publishView: conditional publication of one prepared view into an editable
|
|
19
24
|
// vault. The order is the contract's:
|
|
20
25
|
//
|
|
@@ -317,11 +322,33 @@ export function planUnits({ files, priorManifest, pointer, ledger, pluginDisk =
|
|
|
317
322
|
return units
|
|
318
323
|
}
|
|
319
324
|
|
|
325
|
+
// The code of a failure met while candidates were being cleaned up after a staging that did not complete, for the detail
|
|
326
|
+
// of what ended it. Nothing else of the failure is kept: its message may name a path.
|
|
327
|
+
const cleanupCauseOf = (failure) => (failure === null ? {} : { cleanupCause: typeof failure?.code === 'string' && failure.code !== '' ? failure.code.slice(0, 64) : 'unknown' })
|
|
328
|
+
// The stop or refusal itself is handed on, so the caller can still tell it by identity: its `detail` is replaced by a
|
|
329
|
+
// copy that adds `cleanupCause`, and an object shared as its detail is left as it was. A caller that throws the same
|
|
330
|
+
// error object again keeps that field on it.
|
|
331
|
+
function withCleanupCause(error, failure) {
|
|
332
|
+
if (failure !== null) try { error.detail = { ...(error.detail ?? {}), ...cleanupCauseOf(failure) } } catch { /* an error that cannot be extended is handed on as it is */ }
|
|
333
|
+
return error
|
|
334
|
+
}
|
|
335
|
+
|
|
320
336
|
// The rules the oracle tests are sensitive to. Production always uses these; the tests substitute a deliberately
|
|
321
337
|
// broken one through publishViewForOracleTests to prove the oracle can fail.
|
|
322
338
|
export const PUBLICATION_PRIMITIVES = Object.freeze({
|
|
323
339
|
// The one operation the `direct-unheld` path writes into the vault: a file created where nothing is.
|
|
324
340
|
unheldWrites: (op) => op === 'create',
|
|
341
|
+
// Whether an error that stopped staging was the caller's stop (its settings check, or a cancellation), handed on
|
|
342
|
+
// under its own code once the candidates are cleaned up, rather than reported as a staging failure.
|
|
343
|
+
callerStoppedStaging: (error) => isSchedulingStop(error),
|
|
344
|
+
// Whether a refusal raised while candidates are being staged (the vault's folder replaced or gone under the run) is
|
|
345
|
+
// handed on only once those candidates are cleaned up, as a stop is. A candidate staged before the journal is
|
|
346
|
+
// written is named by no journal, so restart recovery would never remove one left there.
|
|
347
|
+
refusedStagingCleansUp: () => true,
|
|
348
|
+
// The code the journal's last entry gives an error that ended the publication once the journal was open: a
|
|
349
|
+
// caller's stop under its own code, which is what the caller is told, and anything else as a fault of the publisher.
|
|
350
|
+
// Decided by the tag a stop carries, never by what an error looks like; a stop that carries no code has none to give.
|
|
351
|
+
journalFailureCode: (error) => (isSchedulingStop(error) && typeof error.code === 'string' && error.code !== '' ? error.code.slice(0, 200) : 'publisher-error'),
|
|
325
352
|
})
|
|
326
353
|
|
|
327
354
|
// Whether the `direct-unheld` path applies, after the adapter found an app it cannot coordinate with (see the head of
|
|
@@ -355,6 +382,11 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
355
382
|
if (!adapter || typeof adapter.probe !== 'function') throw new TypeError('publishView needs an editor coordination adapter')
|
|
356
383
|
if (expectedGeneration !== null && typeof expectedGeneration !== 'string') throw new TypeError('expectedGeneration must be a generation identity or null')
|
|
357
384
|
|
|
385
|
+
const budget = createCooperativeBudget(options.scheduling)
|
|
386
|
+
budget.check()
|
|
387
|
+
// The caller's guard alone, not the cancellation signal: a cancelled publication still records its failure, and so
|
|
388
|
+
// does one the guard stops for another reason (a changed selection or settings). Only a location refusal suppresses it.
|
|
389
|
+
const locationStillProven = () => { try { options.scheduling?.guard?.(); return true } catch (error) { return !isLocationRefusal(error) } }
|
|
358
390
|
const releases = []
|
|
359
391
|
let journal = null
|
|
360
392
|
try {
|
|
@@ -384,9 +416,16 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
384
416
|
store.checkAllocatedVault?.()
|
|
385
417
|
acquire(store.vaultLockPath, () => acquireVaultLock(store), 'into this vault')
|
|
386
418
|
store.checkAllocatedVault?.()
|
|
419
|
+
await budget.checkpoint('recover', true)
|
|
420
|
+
store.checkAllocatedVault?.()
|
|
387
421
|
const recovered = recoverPublicationsLocked({ store, clock })
|
|
422
|
+
await budget.checkpoint('recovery-complete', true)
|
|
423
|
+
store.checkAllocatedVault?.()
|
|
388
424
|
const pointer = store.readCurrent()
|
|
389
425
|
if (pointer?.generationId === manifest.generationId && !pluginFilesDrifted(preparedView, store)) {
|
|
426
|
+
await options.beforeCommit?.()
|
|
427
|
+
budget.check()
|
|
428
|
+
store.checkAllocatedVault?.()
|
|
390
429
|
return { state: 'committed', alreadyCommitted: true, generationId: manifest.generationId, journalId: pointer.journalId, notes: [], retainedEdits: pointer.retained ?? [], lateWriters: [], recovered }
|
|
391
430
|
}
|
|
392
431
|
if ((pointer?.generationId ?? null) !== expectedGeneration) refuse('generation-mismatch', 'the committed generation is not the one this publication expects', { committed: pointer?.generationId ?? null })
|
|
@@ -394,9 +433,11 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
394
433
|
// Path selection. An app with this vault open in several windows is refused as such: publishing through one of
|
|
395
434
|
// them would leave the others uncoordinated.
|
|
396
435
|
const probe = await adapter.probe({ vaultRoot: store.vaultRoot })
|
|
436
|
+
budget.check()
|
|
397
437
|
store.checkAllocatedVault?.()
|
|
398
438
|
const unheld = probe.state !== 'coordinated' && probe.state !== 'absent' && await unheldPathApplies({ probe, pointer, store, unheldEvidence, platform })
|
|
399
|
-
// The evidence took time to read: the folder
|
|
439
|
+
// The evidence took time to read: the location and the folder are checked again.
|
|
440
|
+
budget.check()
|
|
400
441
|
if (unheld) store.checkAllocatedVault?.()
|
|
401
442
|
if (probe.state !== 'coordinated' && probe.state !== 'absent' && !unheld) refuse(probe.code === 'vault-open-in-several-windows' ? probe.code : 'editor-uncoordinated', `an Obsidian process may have this vault open and cannot be coordinated with: ${probe.reason}`)
|
|
402
443
|
const mode = probe.state === 'coordinated' ? 'in-app' : unheld ? 'direct-unheld' : 'direct'
|
|
@@ -426,12 +467,16 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
426
467
|
stagingDir = store.stagingDir(journalId)
|
|
427
468
|
// On the `direct-unheld` path a replacement is never written, so no candidate is staged for one.
|
|
428
469
|
for (const unit of units.filter((item) => item.op === 'create' || (item.op === 'replace' && (mode !== 'direct-unheld' || rules.unheldWrites(item.op))))) {
|
|
470
|
+
await budget.checkpoint('stage-candidate')
|
|
471
|
+
store.checkAllocatedVault?.()
|
|
429
472
|
const existing = fs.lstatSync(path.join(store.vaultRoot, unit.path), { throwIfNoEntry: false })
|
|
430
473
|
unit.preparedPath = store.preparedPath(journalId, unit.unit)
|
|
431
474
|
stageCandidate(unit.preparedPath, unit.bytes, unit.mode ?? (existing?.isFile() ? existing.mode & 0o777 : 0o644), created)
|
|
432
475
|
// A replacement is exchanged, and the exchange leaves the displaced bytes at this path: it is in recovery.
|
|
433
476
|
unit.stagedPath = unit.op === 'replace' ? store.exchangeCandidatePath(journalId, unit.unit) : unit.preparedPath
|
|
434
477
|
}
|
|
478
|
+
await budget.checkpoint('staging-complete', true)
|
|
479
|
+
store.checkAllocatedVault?.()
|
|
435
480
|
syncPrivateDirectory(stagingDir)
|
|
436
481
|
const manifestFile = path.join(store.journalDir(journalId), 'manifest.json')
|
|
437
482
|
publishPrivateFile(manifestFile, manifestBytes)
|
|
@@ -446,28 +491,49 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
446
491
|
// The journal now names every exchange candidate by path and digest. Only complete, fsynced files are moved
|
|
447
492
|
// in, so a file at an exchange path is never a partial candidate.
|
|
448
493
|
for (const unit of units.filter((item) => item.stagedPath && item.stagedPath !== item.preparedPath)) {
|
|
494
|
+
await budget.checkpoint('move-candidate')
|
|
495
|
+
store.checkAllocatedVault?.()
|
|
449
496
|
moveToExchangePath(store, journalId, unit, moved)
|
|
450
497
|
}
|
|
451
498
|
} catch (error) {
|
|
452
|
-
|
|
499
|
+
const stopped = rules.callerStoppedStaging(error)
|
|
500
|
+
// A refusal raised between two candidates (the publisher's own check found the vault's folder replaced or gone)
|
|
501
|
+
// is handed on under its own code too, and like a stop only after the cleanup below.
|
|
502
|
+
const refused = error instanceof PublicationRefusal && !stopped
|
|
503
|
+
if (refused && !rules.refusedStagingCleansUp(error)) throw error
|
|
453
504
|
// No payload has named any of these paths, so nothing was exchanged and they can only hold our own
|
|
454
505
|
// candidates. In staging they are removed by name. In recovery a file is removed only when its bytes are
|
|
455
506
|
// the candidate's; anything else stays for restart recovery, which the journal header points at it. Only
|
|
456
507
|
// empty directories are removed, so a failed staging leaves nothing that looks like an unfinished unit.
|
|
508
|
+
// Each file is cleaned up on its own, so one that cannot be removed (EBUSY, EPERM, a volume gone) leaves the
|
|
509
|
+
// others removed; the first such failure is kept.
|
|
510
|
+
let cleanupFailure = null
|
|
511
|
+
const cleanUp = (operation) => { try { operation() } catch (failure) { cleanupFailure ??= failure } }
|
|
457
512
|
for (const unit of moved) {
|
|
458
|
-
if (readFileDigest(unit.stagedPath) === unit.candidateDigest) fs.rmSync(unit.stagedPath, { force: true })
|
|
513
|
+
cleanUp(() => { if (readFileDigest(unit.stagedPath) === unit.candidateDigest) fs.rmSync(unit.stagedPath, { force: true }) })
|
|
459
514
|
try { fs.rmdirSync(path.dirname(unit.stagedPath)) } catch { /* not empty: left for restart recovery */ }
|
|
460
515
|
}
|
|
461
|
-
for (const file of created) fs.rmSync(file, { force: true })
|
|
516
|
+
for (const file of created) cleanUp(() => fs.rmSync(file, { force: true }))
|
|
462
517
|
for (const directory of [stagingDir, store.unitsRoot(journalId)]) if (directory) try { fs.rmdirSync(directory) } catch { /* absent, or not empty */ }
|
|
463
|
-
|
|
518
|
+
// The caller stopped the publication between two candidates: nothing failed to stage, and its reason (a
|
|
519
|
+
// setting that changed during the tick, `mixed-read`, or a cancellation) is the one reported. So is a refusal's.
|
|
520
|
+
// A cleanup that failed does not replace either: it is named in their detail as `cleanupCause`, and what it could
|
|
521
|
+
// not remove stays where it is.
|
|
522
|
+
if (stopped || refused) throw withCleanupCause(error, cleanupFailure)
|
|
523
|
+
refuse('staging-failed', 'the candidates could not be staged; nothing in the vault was touched', { cause: error.code ?? String(error.message), ...cleanupCauseOf(cleanupFailure) })
|
|
464
524
|
}
|
|
525
|
+
await budget.checkpoint('journal-and-moves-complete', true)
|
|
526
|
+
store.checkAllocatedVault?.()
|
|
465
527
|
crash('after-staging')
|
|
466
528
|
|
|
467
|
-
|
|
529
|
+
// `check` runs the caller's guard: every wait for the app resumes through it before the next recovery, journal or
|
|
530
|
+
// vault write.
|
|
531
|
+
const context = { store, journal, journalId, channel, clock, crash, mode, rules, check: () => budget.check() }
|
|
468
532
|
const results = []
|
|
469
533
|
// Kept notes are compared first, several at a time; one that holds exactly its candidate bytes is unchanged.
|
|
470
534
|
const unchanged = await unchangedKeepUnits(units, store)
|
|
535
|
+
// The comparison read the disk: the location is proven again before any unit's recovery, journal or vault write.
|
|
536
|
+
budget.check()
|
|
471
537
|
// The direct path has no editor coordination, so it is only right while no
|
|
472
538
|
// Obsidian runs. Staging takes time and an app may have started since path
|
|
473
539
|
// selection: the process table is read again immediately before the first
|
|
@@ -479,14 +545,19 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
479
545
|
// unit refuses, a kept one included, so nothing is committed.
|
|
480
546
|
let lastProbe = null
|
|
481
547
|
for (const unit of units) {
|
|
548
|
+
await budget.checkpoint('publish-unit')
|
|
549
|
+
store.checkAllocatedVault?.()
|
|
482
550
|
if (mode === 'direct' && (lastProbe === null || Date.now() - lastProbe > 2000)) {
|
|
483
551
|
lastProbe = Date.now()
|
|
484
552
|
const again = await adapter.probe({ vaultRoot: store.vaultRoot })
|
|
553
|
+
budget.check()
|
|
485
554
|
if (again.state !== 'absent') context.uncoordinated = again.reason
|
|
486
555
|
}
|
|
487
556
|
if (mode === 'direct-unheld' && !context.uncoordinated && (lastProbe === null || Date.now() - lastProbe > 2000)) {
|
|
488
557
|
lastProbe = Date.now()
|
|
489
|
-
|
|
558
|
+
const listed = await readsUnlisted(unheldEvidence, store)
|
|
559
|
+
budget.check()
|
|
560
|
+
if (!listed) context.uncoordinated = 'the vault list of the app no longer shows, positively, that no Obsidian lists this vault'
|
|
490
561
|
}
|
|
491
562
|
store.checkAllocatedVault?.()
|
|
492
563
|
if (unit.op === 'keep' && unchanged.has(unit.path)) {
|
|
@@ -501,6 +572,8 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
501
572
|
}
|
|
502
573
|
}
|
|
503
574
|
|
|
575
|
+
await budget.checkpoint('publication-complete', true)
|
|
576
|
+
store.checkAllocatedVault?.()
|
|
504
577
|
const blocking = results.filter((result) => result.blocking)
|
|
505
578
|
const retainedEdits = results.filter((result) => result.retained).map((result) => result.retained)
|
|
506
579
|
// Notes kept earlier stay surfaced until they leave the vault or come back into a view.
|
|
@@ -512,11 +585,22 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
512
585
|
|
|
513
586
|
// Quiet period, then look at the displaced files again.
|
|
514
587
|
if (quietPeriodMs > 0 && results.some((result) => result.recoveryRef)) await sleep(quietPeriodMs)
|
|
588
|
+
budget.check()
|
|
515
589
|
const lateWriters = recheckDisplacedFiles({ store, journalIds: [journalId], clock })
|
|
516
590
|
for (const finding of lateWriters.filter((item) => item.code === 'late-writer-captured')) {
|
|
517
591
|
journal.append({ step: 'verify', outcome: 'conflict', state: 'verifying', notePath: finding.notePath, beforeDigest: finding.digestAtMove, afterDigest: finding.observedDigest, recoveryRef: finding.displacedRef,
|
|
518
592
|
detail: { unit: finding.unit, code: 'late-writer-captured', objectRef: finding.objectRef } })
|
|
519
593
|
}
|
|
594
|
+
await budget.checkpoint('verify-before-commit', true)
|
|
595
|
+
// The resident engine revalidates the selected source pins here, while
|
|
596
|
+
// publication still owns its locks. Its check may yield; no durability
|
|
597
|
+
// unit or committed pointer is half-written across that wait. It comes
|
|
598
|
+
// before the entry that says the publication settled: restart recovery
|
|
599
|
+
// commits a journal that holds that entry, so a refusal after it would be
|
|
600
|
+
// committed by the next publication of this view.
|
|
601
|
+
await options.beforeCommit?.()
|
|
602
|
+
budget.check()
|
|
603
|
+
store.checkAllocatedVault?.()
|
|
520
604
|
journal.append({ step: 'verify', outcome: 'ok', state: 'verifying', detail: { settled: true, retained: retainedEdits } })
|
|
521
605
|
crash('before-manifest-commit')
|
|
522
606
|
store.checkAllocatedVault?.()
|
|
@@ -528,7 +612,9 @@ export async function publishViewForOracleTests(options = {}, primitives = PUBLI
|
|
|
528
612
|
return { ...base, state: 'committed', lateWriters }
|
|
529
613
|
} catch (error) {
|
|
530
614
|
if (!(error instanceof PublicationRefusal)) {
|
|
531
|
-
|
|
615
|
+
// A private-state location refusal records nothing more there, a failure entry included, and neither does a
|
|
616
|
+
// failure that leaves the location unproven now (a rejected wait for the app). Locks are still released.
|
|
617
|
+
if (journal && !isLocationRefusal(error) && locationStillProven()) try { journal.append({ step: 'verify', outcome: 'failed', state: 'failed', detail: { code: rules.journalFailureCode(error), message: String(error.code ?? error.message).slice(0, 200) } }) } catch { /* the journal itself cannot be written */ }
|
|
532
618
|
throw error
|
|
533
619
|
}
|
|
534
620
|
return { state: 'refused', refusal: { code: error.code, message: error.message, detail: error.detail }, notes: [], retainedEdits: [], lateWriters: [] }
|
|
@@ -705,6 +791,7 @@ async function publishOneUnit(unit, context) {
|
|
|
705
791
|
}
|
|
706
792
|
const baseObject = observe(current)
|
|
707
793
|
const inspected = await context.channel.inspect({ vaultRoot: store.vaultRoot, path: unit.path })
|
|
794
|
+
context.check?.()
|
|
708
795
|
if (inspected.status !== 'inspected') {
|
|
709
796
|
capture('failed', inspected.status, { beforeDigest: currentDigest })
|
|
710
797
|
if (plan.stagedPath) retire(plan, context)
|
|
@@ -723,6 +810,7 @@ async function publishOneUnit(unit, context) {
|
|
|
723
810
|
? { op: 'publish', mode: 'remove', vaultRoot: store.vaultRoot, path: unit.path, operationId, baseSha256: hex(plan.baseDigest), recoveryPath: displaced }
|
|
724
811
|
: { op: 'publish', mode: 'replace', vaultRoot: store.vaultRoot, path: unit.path, operationId, baseSha256: hex(plan.baseDigest), candidateSha256: hex(plan.candidateDigest), stagedPath: plan.stagedPath, recoveryPath: displaced })
|
|
725
812
|
context.crash('after-publish')
|
|
813
|
+
context.check?.()
|
|
726
814
|
|
|
727
815
|
if (reply.status === 'published' || reply.status === 'published-external-captured' || reply.status === 'removed' || reply.status === 'removed-external-captured') {
|
|
728
816
|
syncPrivateDirectory(path.dirname(displaced))
|
|
@@ -3,6 +3,8 @@ import { isDeepStrictEqual } from 'node:util'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { commandProject, readJson, writeJson } from '../project/config.mjs'
|
|
5
5
|
import { summarizeReadinessJourney } from '../readiness-protocols/runtime.mjs'
|
|
6
|
+
import { bundledReadinessEnabled } from '../readiness-protocols/bundled-selection.mjs'
|
|
7
|
+
import { atelierControlStyles } from '../ui/control-system.mjs'
|
|
6
8
|
|
|
7
9
|
const esc = (value) => String(value ?? '').replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"')
|
|
8
10
|
|
|
@@ -44,16 +46,17 @@ function resolveDistributionBranding(project) {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export function buildProjectProjection(project) {
|
|
49
|
+
const includeBundled = bundledReadinessEnabled(project)
|
|
47
50
|
const graph = readJson(project.graphPath)
|
|
48
51
|
const branding = resolveDistributionBranding(project)
|
|
49
|
-
const readinessJourney = summarizeReadinessJourney(project)
|
|
52
|
+
const readinessJourney = includeBundled ? summarizeReadinessJourney(project) : null
|
|
50
53
|
const output = path.join(project.outputRoot, 'index.html')
|
|
51
|
-
const dimensionCards = readinessJourney
|
|
54
|
+
const dimensionCards = (readinessJourney?.dimensions ?? []).map((item) => `<article class="readiness-card" data-protocol="${esc(item.protocolId)}">
|
|
52
55
|
<p class="eyebrow">${esc(item.label)}</p>
|
|
53
56
|
<h3>${esc(item.title)}</h3>
|
|
54
57
|
<p><strong>${esc(item.status)}</strong> · ${esc(item.score)} / 100 answers complete</p>
|
|
55
58
|
<p>${esc(item.blockers[0] || 'review-ready')}</p>
|
|
56
|
-
<button type="button" data-copy="${esc(item.agentPrompt)}">Copy protocol prompt</button>
|
|
59
|
+
<button type="button" class="system-control" data-variant="secondary" data-size="compact" data-copy="${esc(item.agentPrompt)}">Copy protocol prompt</button>
|
|
57
60
|
</article>`).join('\n')
|
|
58
61
|
const cards = graph.nodes.map((node) => `<article class="card" data-node="${esc(node.id)}">
|
|
59
62
|
<p class="eyebrow">${esc(node.repo)} · ${esc(node.type)} · ${esc(node.audience)}</p>
|
|
@@ -80,16 +83,16 @@ h1{font-size:clamp(2rem,4vw,4rem);margin:.2em 0}
|
|
|
80
83
|
.readiness-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:16px}
|
|
81
84
|
.readiness-card{border:1px solid #342b26;border-radius:8px;padding:14px;background:#171312}
|
|
82
85
|
.readiness-card h3{font-size:1.05rem;margin:.2rem 0}
|
|
83
|
-
.readiness-card button{min-height:44px;border:1px solid #755f43;border-radius:6px;background:#241d18;color:var(--atelier-text);padding:8px 10px;cursor:pointer}
|
|
84
86
|
.eyebrow{color:var(--atelier-eyebrow);text-transform:uppercase;letter-spacing:.08em;font-size:.8rem}
|
|
85
87
|
a{color:var(--atelier-accent)}
|
|
88
|
+
${atelierControlStyles}
|
|
86
89
|
${branding.themeCss}</style>
|
|
87
90
|
</head>
|
|
88
91
|
<body><main>
|
|
89
92
|
<p class="eyebrow">${esc(branding.name)} · ${esc(branding.eyebrow)}</p>
|
|
90
93
|
<h1>${esc(project.config.name || 'Project Workspace')}</h1>
|
|
91
94
|
<p>${graph.counts.nodes} graph nodes · ${graph.counts.edges} graph edges · ${graph.counts.diagnostics} diagnostics</p>
|
|
92
|
-
|
|
95
|
+
${readinessJourney ? `<section class="readiness" aria-labelledby="tenant-readiness-title">
|
|
93
96
|
<div class="readiness-head">
|
|
94
97
|
<div>
|
|
95
98
|
<p class="eyebrow">MNSTRY Tenant Readiness</p>
|
|
@@ -100,7 +103,7 @@ ${branding.themeCss}</style>
|
|
|
100
103
|
<div class="readiness-score">${esc(readinessJourney.score)}%</div>
|
|
101
104
|
</div>
|
|
102
105
|
<div class="readiness-grid">${dimensionCards}</div>
|
|
103
|
-
</section
|
|
106
|
+
</section>` : ''}
|
|
104
107
|
<section class="grid">${cards}</section>
|
|
105
108
|
<script>
|
|
106
109
|
document.addEventListener('click', async (event) => {
|
|
@@ -125,16 +128,18 @@ export function buildProjectManifest(project, projection) {
|
|
|
125
128
|
generatedAt: 'deterministic',
|
|
126
129
|
graphPath: path.relative(project.outputRoot, project.graphPath).split(path.sep).join('/'),
|
|
127
130
|
entry: 'index.html',
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
...(projection.readinessJourney ? {
|
|
132
|
+
tenantReadiness: {
|
|
133
|
+
score: projection.readinessJourney.score,
|
|
134
|
+
ready: projection.readinessJourney.ready,
|
|
135
|
+
dimensions: projection.readinessJourney.dimensions.map((item) => ({
|
|
136
|
+
key: item.key,
|
|
137
|
+
protocolId: item.protocolId,
|
|
138
|
+
status: item.status,
|
|
139
|
+
score: item.score,
|
|
140
|
+
})),
|
|
141
|
+
},
|
|
142
|
+
} : {}),
|
|
138
143
|
nodes: projection.graph.nodes.map((node) => ({ id: node.id, title: node.title, audience: node.audience, path: node.path })),
|
|
139
144
|
}
|
|
140
145
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { bundledReadinessProtocols } from '../readiness-protocols/bundled-pack.mjs'
|
|
4
|
+
import { bundledReadinessEnabled, requireBundledReadiness, requireSelectedReadinessProtocol } from '../readiness-protocols/bundled-selection.mjs'
|
|
4
5
|
import { createProtocolRegistry, loadExtensionPacks } from '../extension-packs/loader.mjs'
|
|
5
6
|
import {
|
|
6
7
|
buildReadinessExportDryRun,
|
|
@@ -131,6 +132,7 @@ function configRelative(project, file) {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
function buildProjectReadiness({ project, graph = null } = {}) {
|
|
135
|
+
const includeBundled = bundledReadinessEnabled(project)
|
|
134
136
|
const loadedGraph = graph || (fs.existsSync(project.graphPath) ? readJson(project.graphPath) : null)
|
|
135
137
|
const projectionEntry = path.join(project.outputRoot, 'index.html')
|
|
136
138
|
const manifest = path.join(project.outputRoot, 'atelier.manifest.json')
|
|
@@ -167,7 +169,7 @@ function buildProjectReadiness({ project, graph = null } = {}) {
|
|
|
167
169
|
enabled: false,
|
|
168
170
|
authority: 'claim-only',
|
|
169
171
|
},
|
|
170
|
-
tenantReadiness: summarizeReadinessJourney(project),
|
|
172
|
+
...(includeBundled ? { tenantReadiness: summarizeReadinessJourney(project) } : {}),
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
|
|
@@ -201,10 +203,11 @@ export function stableReadinessForCheck(value) {
|
|
|
201
203
|
|
|
202
204
|
// Loads declared extension packs (fail closed: any pack error throws before a
|
|
203
205
|
// protocol becomes resolvable) and composes the explicit registry. Projects
|
|
204
|
-
//
|
|
206
|
+
// without an explicit selection retain the bundled default.
|
|
205
207
|
function projectRegistry(project) {
|
|
208
|
+
const includeBundled = bundledReadinessEnabled(project)
|
|
206
209
|
const { packs } = loadExtensionPacks(project)
|
|
207
|
-
return createProtocolRegistry({ packs })
|
|
210
|
+
return createProtocolRegistry({ packs, includeBundled })
|
|
208
211
|
}
|
|
209
212
|
|
|
210
213
|
export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
@@ -219,15 +222,16 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
219
222
|
const envConfig = process.env[PROJECT_CONFIG_ENV]
|
|
220
223
|
const projectAware = projectConfigArg(argv) !== null
|
|
221
224
|
|| (typeof envConfig === 'string' && envConfig.trim() !== '')
|
|
222
|
-
|
|
225
|
+
let includeBundled = true
|
|
226
|
+
let entries = bundledReadinessProtocols.map((protocol) => ({ protocol, source: null }))
|
|
223
227
|
if (projectAware) {
|
|
224
228
|
const project = commandProject({ argv })
|
|
225
229
|
const registry = projectRegistry(project)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
230
|
+
includeBundled = bundledReadinessEnabled(project)
|
|
231
|
+
entries = registry.listProtocols().map((protocol) => ({
|
|
232
|
+
protocol,
|
|
233
|
+
source: registry.packs.find((pack) => pack.protocols?.some((record) => record.id === protocol.id))?.id ?? null,
|
|
234
|
+
}))
|
|
231
235
|
}
|
|
232
236
|
if (detail) {
|
|
233
237
|
console.log(`${JSON.stringify({
|
|
@@ -235,7 +239,7 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
235
239
|
protocols: entries.map(({ protocol, source }) => (source ? { ...protocol, source } : protocol)),
|
|
236
240
|
}, null, 2)}\n`)
|
|
237
241
|
} else {
|
|
238
|
-
console.log('MNSTRY readiness protocols')
|
|
242
|
+
console.log(includeBundled ? 'MNSTRY readiness protocols' : 'Atelier readiness protocols')
|
|
239
243
|
for (const { protocol, source } of entries) {
|
|
240
244
|
console.log(`- ${protocol.id}: ${protocol.title}${source ? ` (source: ${source})` : ''}`)
|
|
241
245
|
}
|
|
@@ -245,6 +249,7 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
245
249
|
|
|
246
250
|
if (subcommand === 'journey') {
|
|
247
251
|
const project = commandProject({ argv })
|
|
252
|
+
requireBundledReadiness(project)
|
|
248
253
|
const journey = summarizeReadinessJourney(project, { registry: projectRegistry(project) })
|
|
249
254
|
console.log(`${JSON.stringify(journey, null, 2)}\n`)
|
|
250
255
|
return
|
|
@@ -254,12 +259,14 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
254
259
|
const project = commandProject({ argv })
|
|
255
260
|
const protocolId = parsed._[1]
|
|
256
261
|
if (!protocolId) throw new Error('readiness run requires a protocol id')
|
|
262
|
+
const registry = projectRegistry(project)
|
|
263
|
+
requireSelectedReadinessProtocol(project, registry, protocolId)
|
|
257
264
|
const answers = parsed.answers ? readJson(path.resolve(process.cwd(), parsed.answers)) : {}
|
|
258
265
|
const result = runProtocol(project, protocolId, {
|
|
259
266
|
answers,
|
|
260
267
|
write: parsed.write !== false,
|
|
261
268
|
createProposal: parsed.proposal !== false && parsed['no-proposal'] !== true,
|
|
262
|
-
registry
|
|
269
|
+
registry,
|
|
263
270
|
})
|
|
264
271
|
console.log(`${JSON.stringify({
|
|
265
272
|
ok: result.run.blockers.length === 0,
|
|
@@ -279,6 +286,7 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
279
286
|
|
|
280
287
|
if (subcommand === 'packet') {
|
|
281
288
|
const project = commandProject({ argv })
|
|
289
|
+
requireBundledReadiness(project)
|
|
282
290
|
const packet = buildTenantPacket(project, { registry: projectRegistry(project) })
|
|
283
291
|
const file = writeTenantPacket(project, packet)
|
|
284
292
|
console.log(`${JSON.stringify({
|
|
@@ -292,6 +300,7 @@ export function runReadinessCommand(argv = process.argv.slice(2)) {
|
|
|
292
300
|
if (subcommand === 'export') {
|
|
293
301
|
if (!argv.includes('--dry-run')) throw new Error('readiness export only supports --dry-run')
|
|
294
302
|
const project = commandProject({ argv })
|
|
303
|
+
requireBundledReadiness(project)
|
|
295
304
|
const report = buildReadinessExportDryRun(project, { registry: projectRegistry(project) })
|
|
296
305
|
console.log(`${JSON.stringify(report, null, 2)}\n`)
|
|
297
306
|
if (!report.accepted) process.exitCode = 1
|