@mnstry/atelier 0.2.0-alpha.1 → 0.2.0-alpha.11
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 +413 -0
- package/README.md +402 -205
- package/SECURITY.md +6 -1
- package/contracts/atelier-adoption-policy.v1.schema.json +49 -0
- package/contracts/atelier-coauthor.v1.schema.json +163 -0
- package/contracts/atelier-decision-request.v1.schema.json +476 -0
- package/contracts/atelier-decision-result.v1.schema.json +500 -0
- package/contracts/atelier-guide.v1.schema.json +227 -0
- package/contracts/atelier-intake.v1.schema.json +151 -0
- package/contracts/atelier-migration.v2.schema.json +61 -0
- package/contracts/atelier-obsidian-acceptance-receipt.v1.schema.json +211 -0
- package/contracts/atelier-obsidian-apply-policy.v1.schema.json +298 -0
- package/contracts/atelier-obsidian-corpus-profile.v1.schema.json +98 -0
- package/contracts/atelier-obsidian-edit-operation.v1.schema.json +138 -0
- package/contracts/atelier-obsidian-ext-settings.v1.schema.json +287 -0
- package/contracts/atelier-obsidian-generation-manifest.v1.schema.json +353 -0
- package/contracts/atelier-obsidian-proposal-receipt.v1.schema.json +110 -0
- package/contracts/atelier-obsidian-publication-journal.v1.schema.json +167 -0
- package/contracts/atelier-obsidian-scope.v1.schema.json +264 -0
- package/contracts/atelier-obsidian-service-state.v1.schema.json +149 -0
- package/contracts/atelier-obsidian-source-snapshot.v1.schema.json +179 -0
- package/contracts/atelier-pane-presentation.v1.schema.json +76 -0
- package/contracts/atelier-presentation.v1.schema.json +1293 -0
- package/contracts/atelier-repository-observation.v1.schema.json +163 -0
- package/contracts/atelier-review.v1.schema.json +1601 -0
- package/contracts/atelier-skill-steward.v1.schema.json +692 -0
- package/contracts/atelier-upgrade-plan.v2.schema.json +398 -0
- package/contracts/atelier-upgrade-receipt.v1.schema.json +99 -0
- package/contracts/atelier-vault-publication.v1.schema.json +58 -0
- package/contracts/public-api-baseline.json +57 -0
- package/docs/assurance-controls.md +49 -0
- package/docs/atelier-runtime.md +28 -2
- package/docs/atelier-sync.md +172 -0
- package/docs/blocks/audience-visibility.md +9 -0
- package/docs/blocks/claims.md +61 -0
- package/docs/blocks/conformance-admission.md +15 -0
- package/docs/blocks/will-not-do.md +18 -0
- package/docs/coauthor-session.md +57 -0
- package/docs/connected-composition.md +75 -0
- package/docs/continuity.md +3 -4
- package/docs/decisions.md +167 -0
- package/docs/design.md +167 -0
- package/docs/exact-upgrades.md +159 -0
- package/docs/extension-pack-lifecycle.md +33 -0
- package/docs/guided-upgrades.md +99 -0
- package/docs/install.md +99 -16
- package/docs/intake-and-guides.md +65 -0
- package/docs/integration-and-review-plan.md +409 -0
- package/docs/integration-contract-decisions.md +22 -0
- package/docs/knowledge-graph.md +8 -4
- package/docs/local-review.md +84 -0
- package/docs/local-services.md +328 -0
- package/docs/local-state.md +107 -0
- package/docs/obsidian-contract.md +698 -0
- package/docs/obsidian.md +484 -0
- package/docs/portable-presentation.md +328 -0
- package/docs/presentation-consumer-boundaries.md +75 -0
- package/docs/presentation-review-disposition.md +47 -0
- package/docs/project-command-inventory.md +38 -0
- package/docs/project-options.md +53 -0
- package/docs/release-engineering.md +174 -12
- package/docs/repo-boundary-guard.md +12 -2
- package/docs/review-portability.md +55 -0
- package/docs/skill-steward.md +156 -0
- package/docs/upgrade.md +155 -5
- package/docs/vault-service.md +292 -0
- package/fixtures/atelier-coauthor/invalid/authority.v1.json +5 -0
- package/fixtures/atelier-coauthor/valid/config.v1.json +12 -0
- package/fixtures/atelier-guide/invalid/authority.v1.json +11 -0
- package/fixtures/atelier-guide/valid/capability.v1.json +10 -0
- package/fixtures/atelier-guide/valid/consent.v1.json +11 -0
- package/fixtures/atelier-guide/valid/engagement.v1.json +7 -0
- package/fixtures/atelier-guide/valid/offer.v1.json +10 -0
- package/fixtures/atelier-intake/invalid/authority.v1.json +9 -0
- package/fixtures/atelier-intake/valid/attempt.v1.json +8 -0
- package/fixtures/atelier-intake/valid/completion.v1.json +9 -0
- package/fixtures/atelier-intake/valid/source.v1.json +7 -0
- package/fixtures/atelier-repository-observation/invalid/complete-with-blocker.v1.json +18 -0
- package/fixtures/atelier-repository-observation/valid/complete-local.v1.json +48 -0
- package/fixtures/atelier-review/invalid/authenticated-identity.v1.json +22 -0
- package/fixtures/atelier-review/valid/bound.v1.json +229 -0
- package/fixtures/atelier-review/valid/bundle.v1.json +267 -0
- package/fixtures/atelier-review/valid/contribution.v1.json +22 -0
- package/fixtures/atelier-review/valid/lifecycle.v1.json +14 -0
- package/fixtures/atelier-skill-steward/audit/invalid/source-mutation.v1.json +26 -0
- package/fixtures/atelier-skill-steward/audit/valid/clean.v1.json +26 -0
- package/fixtures/atelier-skill-steward/candidates/invalid/workflow-key.v1.json +37 -0
- package/fixtures/atelier-skill-steward/candidates/valid/create.v1.json +37 -0
- package/fixtures/atelier-skill-steward/lock/invalid/bad-digest.v1.json +18 -0
- package/fixtures/atelier-skill-steward/lock/valid/installed.v1.json +23 -0
- package/fixtures/atelier-skill-steward/plan/invalid/delete-action.v1.json +33 -0
- package/fixtures/atelier-skill-steward/plan/valid/add.v1.json +33 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/invalid/example.json +12 -0
- package/fixtures/atelier-upgrade-transaction/adoption-policy/valid/example.json +11 -0
- package/fixtures/atelier-upgrade-transaction/migration/invalid/example.json +20 -0
- package/fixtures/atelier-upgrade-transaction/migration/valid/example.json +19 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/invalid/example.json +58 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-plan/valid/example.json +57 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/invalid/example.json +15 -0
- package/fixtures/atelier-upgrade-transaction/upgrade-receipt/valid/example.json +14 -0
- package/fixtures/atelier-vault-publication/invalid/publication.json +5 -0
- package/fixtures/atelier-vault-publication/valid/publication.json +11 -0
- package/fixtures/decisions/request/invalid/unknown-authority.v1.json +61 -0
- package/fixtures/decisions/request/valid/triage.v1.json +60 -0
- package/fixtures/decisions/result/invalid/execution-authority.v1.json +50 -0
- package/fixtures/decisions/result/valid/abstained.v1.json +23 -0
- package/fixtures/decisions/result/valid/assessed.v1.json +50 -0
- package/fixtures/obsidian/acceptance/ap05-extra-notes.json +14 -0
- package/fixtures/obsidian/acceptance/receipts/G07.valid.v1.json +63 -0
- package/fixtures/obsidian/acceptance/receipts/G13.valid.v1.json +69 -0
- package/fixtures/obsidian/acceptance/receipts/G14.valid.v1.json +62 -0
- package/fixtures/obsidian/acceptance/receipts/G15.valid.v1.json +56 -0
- package/fixtures/obsidian/acceptance/receipts/G16.valid.v1.json +73 -0
- package/fixtures/obsidian/acceptance/receipts/G17.valid.v1.json +75 -0
- package/fixtures/obsidian/acceptance/receipts/G18.valid.v1.json +64 -0
- package/fixtures/obsidian/acceptance/service-sentinels.json +10 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/duplicate-evidence-name.v1.json +39 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/evidence-by-absolute-path.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/missing-candidate-tree.v1.json +33 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/no-evidence.v1.json +28 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-outcome.v1.json +34 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/invalid/unknown-top-level-field.v1.json +35 -0
- package/fixtures/obsidian/contracts/acceptance-receipt/valid/node-gate.v1.json +34 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/automatic-without-edit-class.v1.json +27 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/conflict-overwrite.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/missing-policy-digest.v1.json +28 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unbounded-batch.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unimplemented-edit-class.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-mode.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-selector-operator.v1.json +22 -0
- package/fixtures/obsidian/contracts/apply-policy/invalid/unknown-top-level-field.v1.json +30 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/automatic-scoped.v1.json +29 -0
- package/fixtures/obsidian/contracts/apply-policy/valid/manual-default.v1.json +20 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-path-in-ext.v1.json +30 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/absolute-repository-root.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/duplicate-repository-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/overlapping-managed-roots.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-enrollment.v1.json +27 -0
- package/fixtures/obsidian/contracts/corpus-profile/invalid/unknown-top-level-field.v1.json +28 -0
- package/fixtures/obsidian/contracts/corpus-profile/valid/three-repositories.v1.json +27 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/absolute-recovery-ref.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/inline-observed-bytes.v1.json +22 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/missing-base-digest.v1.json +20 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/short-idempotency-key.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/invalid/unknown-kind.v1.json +21 -0
- package/fixtures/obsidian/contracts/edit-operation/valid/pending-body-replacement.v1.json +21 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/absolute-path-prefix.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/apply-policy-in-portable-settings.v1.json +30 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/duplicate-scope-id.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/machine-local-vault-path.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/service-port-in-portable-settings.v1.json +28 -0
- package/fixtures/obsidian/contracts/ext-settings/invalid/unknown-default-scope.v1.json +27 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/disabled.v1.json +5 -0
- package/fixtures/obsidian/contracts/ext-settings/valid/enabled-with-scopes.v1.json +27 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/absolute-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/complete-with-unwritten-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/derived-link-claimed-declared.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/duplicate-note-identity.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/in-scope-endpoint-missing.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/title-only-note-path.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-note-field.v1.json +115 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/unknown-relation-type.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/invalid/withheld-endpoint.v1.json +114 -0
- package/fixtures/obsidian/contracts/generation-manifest/valid/two-notes.v1.json +114 -0
- package/fixtures/obsidian/contracts/oracles/scope-cases.json +412 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/accepted-without-proposal-id.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/missing-adapter-operation-id.v1.json +11 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-backpressure-outcome.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/invalid/unknown-top-level-field.v1.json +13 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/accepted-new.v1.json +12 -0
- package/fixtures/obsidian/contracts/proposal-receipt/valid/deferred.v1.json +12 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/absolute-recovery-ref.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/duplicate-sequence.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/empty-protocol-id.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/missing-protocol-id.v1.json +46 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-entry-field.v1.json +48 -0
- package/fixtures/obsidian/contracts/publication-journal/invalid/unknown-state.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/committed.v1.json +47 -0
- package/fixtures/obsidian/contracts/publication-journal/valid/updating-after-restart.v1.json +22 -0
- package/fixtures/obsidian/contracts/scope/invalid/absolute-path-prefix.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/duplicate-ids.v1.json +18 -0
- package/fixtures/obsidian/contracts/scope/invalid/expansion-without-node-budget.v1.json +40 -0
- package/fixtures/obsidian/contracts/scope/invalid/full-mode-with-subset.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-mode.v1.json +41 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-key.v1.json +15 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-selector-operator.v1.json +14 -0
- package/fixtures/obsidian/contracts/scope/invalid/unknown-top-level-field.v1.json +42 -0
- package/fixtures/obsidian/contracts/scope/valid/explicit-empty.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/focus.v1.json +11 -0
- package/fixtures/obsidian/contracts/scope/valid/full.v1.json +8 -0
- package/fixtures/obsidian/contracts/scope/valid/scoped-set-expression.v1.json +41 -0
- package/fixtures/obsidian/contracts/service-state/invalid/hostname-instead-of-literal-loopback.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/missing-consent.v1.json +18 -0
- package/fixtures/obsidian/contracts/service-state/invalid/privileged-port.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/invalid/unknown-top-level-field.v1.json +24 -0
- package/fixtures/obsidian/contracts/service-state/invalid/wildcard-bind.v1.json +23 -0
- package/fixtures/obsidian/contracts/service-state/valid/healthy.v1.json +23 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/absolute-file-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/duplicate-repository-id.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/mixed-read.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/parent-traversal-path.v1.json +48 -0
- package/fixtures/obsidian/contracts/source-snapshot/invalid/unknown-file-field.v1.json +49 -0
- package/fixtures/obsidian/contracts/source-snapshot/valid/two-repositories.v1.json +48 -0
- package/fixtures/obsidian/edits/workspace.json +65 -0
- package/fixtures/obsidian/materialization/expected-full.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +39 -0
- package/fixtures/obsidian/materialization/workspace.json +54 -0
- package/fixtures/obsidian/proposals/expected-shapes.json +11 -0
- package/fixtures/projects/sample-workspace/content/source.html.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +4 -1
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +4 -1
- package/fixtures/ui/presentation/pane.v1.json +7 -0
- package/fixtures/ui/presentation/reference.v1.json +293 -0
- package/package.json +86 -10
- package/skills/claude/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/claude/atelier-local-service/SKILL.md +47 -0
- package/skills/claude/atelier-open/SKILL.md +11 -5
- package/skills/claude/atelier-public-boundary/SKILL.md +31 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +62 -0
- package/skills/claude/mnstry-readiness/SKILL.md +5 -0
- package/skills/codex/atelier-guided-coauthor/SKILL.md +49 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +97 -0
- package/skills/codex/atelier-local-service/SKILL.md +47 -0
- package/skills/codex/atelier-open/SKILL.md +11 -5
- package/skills/codex/atelier-public-boundary/SKILL.md +31 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +62 -0
- package/skills/codex/mnstry-readiness/SKILL.md +5 -0
- package/src/access/preview-enforcer.mjs +84 -0
- package/src/boundary/content-rules.mjs +283 -20
- package/src/boundary/policy.mjs +213 -91
- package/src/cli/execute-command.mjs +36 -0
- package/src/cli/project-options.mjs +58 -0
- package/src/cli/run.mjs +78 -10
- package/src/coauthor/session.mjs +161 -0
- package/src/coauthor/store.mjs +173 -0
- package/src/collaboration/event-ledger.mjs +348 -0
- package/src/collaboration/index.mjs +17 -0
- package/src/collaboration/inspection-bundle.mjs +223 -0
- package/src/collaboration/proposals.mjs +265 -65
- package/src/collaboration/review-contracts.mjs +25 -0
- package/src/collaboration/review-store.mjs +305 -0
- package/src/commands/attestation.mjs +20 -6
- package/src/commands/coauthor.mjs +26 -0
- package/src/commands/config.mjs +6 -1
- package/src/commands/disclosure.mjs +133 -0
- package/src/commands/distribution.mjs +2 -1
- package/src/commands/extension-pack.mjs +17 -9
- package/src/commands/init.mjs +46 -6
- package/src/commands/obsidian.mjs +371 -0
- package/src/commands/review.mjs +147 -0
- package/src/commands/server.mjs +1 -4
- package/src/commands/setup.mjs +51 -14
- package/src/commands/skills.mjs +199 -0
- package/src/commands/sync.mjs +107 -0
- package/src/composition/registry.mjs +27 -0
- package/src/composition/wire.mjs +122 -0
- package/src/contracts/corpus.mjs +35 -0
- package/src/decisions/contracts.d.mts +96 -0
- package/src/decisions/contracts.mjs +321 -0
- package/src/disclosure/content-scan.mjs +206 -0
- package/src/egress/check.mjs +7 -38
- package/src/egress/forbidden-egress.mjs +32 -18
- package/src/export/atelier-export-contract.mjs +16 -1
- package/src/extension-packs/lifecycle.mjs +154 -0
- package/src/graph/graph.mjs +176 -316
- package/src/graph/knowledge-graph.mjs +643 -43
- package/src/guides/contracts.mjs +54 -0
- package/src/harness/context-client.mjs +9 -1
- package/src/index.mjs +41 -0
- package/src/intake/store.mjs +119 -0
- package/src/preview/channel.mjs +89 -0
- package/src/project/config.mjs +112 -52
- package/src/project/durable-state.mjs +161 -0
- package/src/project/file-class.mjs +180 -0
- package/src/project/package-root.mjs +10 -0
- package/src/project/path-match.mjs +38 -15
- package/src/project/private-state.mjs +120 -0
- package/src/projection/obsidian/contracts.mjs +571 -0
- package/src/projection/obsidian/edits/align.mjs +169 -0
- package/src/projection/obsidian/edits/apply.mjs +889 -0
- package/src/projection/obsidian/edits/arbitrate.mjs +414 -0
- package/src/projection/obsidian/edits/contribution.mjs +108 -0
- package/src/projection/obsidian/edits/index.mjs +19 -0
- package/src/projection/obsidian/edits/object-identity.mjs +61 -0
- package/src/projection/obsidian/edits/object-store.mjs +517 -0
- package/src/projection/obsidian/edits/observe.mjs +123 -0
- package/src/projection/obsidian/edits/policy.mjs +123 -0
- package/src/projection/obsidian/edits/regions.mjs +648 -0
- package/src/projection/obsidian/materialize/byte-lens.mjs +98 -0
- package/src/projection/obsidian/materialize/index.mjs +7 -0
- package/src/projection/obsidian/materialize/path-registry.mjs +105 -0
- package/src/projection/obsidian/materialize/prepare-view.mjs +746 -0
- package/src/projection/obsidian/materialize/settings.mjs +57 -0
- package/src/projection/obsidian/materialize/stage.mjs +27 -0
- package/src/projection/obsidian/proposals/adapter.mjs +572 -0
- package/src/projection/obsidian/proposals/backpressure.mjs +113 -0
- package/src/projection/obsidian/proposals/contribution.mjs +55 -0
- package/src/projection/obsidian/proposals/index.mjs +23 -0
- package/src/projection/obsidian/proposals/observation.mjs +216 -0
- package/src/projection/obsidian/proposals/queue.mjs +277 -0
- package/src/projection/obsidian/proposals/router.mjs +168 -0
- package/src/projection/obsidian/publication/bridge-script.mjs +266 -0
- package/src/projection/obsidian/publication/exchange.mjs +117 -0
- package/src/projection/obsidian/publication/index.mjs +5 -0
- package/src/projection/obsidian/publication/publisher.mjs +588 -0
- package/src/projection/obsidian/publication/test-seam.mjs +16 -0
- package/src/projection/obsidian/publication/transport.mjs +212 -0
- package/src/projection/obsidian/recovery/index.mjs +6 -0
- package/src/projection/obsidian/recovery/journal.mjs +134 -0
- package/src/projection/obsidian/recovery/late-writer.mjs +46 -0
- package/src/projection/obsidian/recovery/restart.mjs +316 -0
- package/src/projection/obsidian/recovery/store.mjs +234 -0
- package/src/projection/obsidian/selection-ui/conflict-view.mjs +91 -0
- package/src/projection/obsidian/selection-ui/contribution.mjs +141 -0
- package/src/projection/obsidian/selection-ui/focus.mjs +82 -0
- package/src/projection/obsidian/selection-ui/index.mjs +12 -0
- package/src/projection/obsidian/selection-ui/policy-setup.mjs +107 -0
- package/src/projection/obsidian/selection-ui/receipt.mjs +173 -0
- package/src/projection/obsidian/selection-ui/selection-state.mjs +122 -0
- package/src/projection/obsidian/selection-ui/selection.mjs +109 -0
- package/src/projection/project.mjs +28 -19
- package/src/readiness-protocols/evidence.mjs +282 -0
- package/src/readiness-protocols/runtime.mjs +4 -3
- package/src/readiness-protocols/source-read.mjs +39 -0
- package/src/runtime/git-adapter.mjs +189 -0
- package/src/runtime/local-state.mjs +439 -0
- package/src/runtime/obsidian/app-capability.mjs +138 -0
- package/src/runtime/obsidian/app-production-seams.mjs +107 -0
- package/src/runtime/obsidian/contributions/proposal-adapter.mjs +4 -0
- package/src/runtime/obsidian/contributions/selection-ui.mjs +4 -0
- package/src/runtime/obsidian/contributions/source-apply.mjs +4 -0
- package/src/runtime/obsidian/contributions.mjs +27 -0
- package/src/runtime/obsidian/documents.mjs +37 -0
- package/src/runtime/obsidian/enablement.mjs +53 -0
- package/src/runtime/obsidian/engine.mjs +532 -0
- package/src/runtime/obsidian/errors.mjs +14 -0
- package/src/runtime/obsidian/extension-points.mjs +103 -0
- package/src/runtime/obsidian/index.mjs +34 -0
- package/src/runtime/obsidian/lifecycle.mjs +258 -0
- package/src/runtime/obsidian/machine-settings.mjs +260 -0
- package/src/runtime/obsidian/observation.mjs +158 -0
- package/src/runtime/obsidian/opening.mjs +280 -0
- package/src/runtime/obsidian/pending-edits.mjs +141 -0
- package/src/runtime/obsidian/pipeline.mjs +122 -0
- package/src/runtime/obsidian/private-lock.mjs +156 -0
- package/src/runtime/obsidian/process-identity.mjs +58 -0
- package/src/runtime/obsidian/service-client.mjs +66 -0
- package/src/runtime/obsidian/service-entry-path.mjs +9 -0
- package/src/runtime/obsidian/service-main.mjs +93 -0
- package/src/runtime/obsidian/service-record.mjs +169 -0
- package/src/runtime/obsidian/service-server.mjs +121 -0
- package/src/runtime/obsidian/service.mjs +216 -0
- package/src/runtime/obsidian/startup-adapters.mjs +87 -0
- package/src/runtime/obsidian/state-store.mjs +180 -0
- package/src/runtime/obsidian/sync-notice.mjs +33 -0
- package/src/runtime/obsidian/tick-loop.mjs +84 -0
- package/src/runtime/obsidian/watchers.mjs +36 -0
- package/src/runtime/repository-observation.mjs +491 -0
- package/src/runtime/supervisor.mjs +803 -0
- package/src/server/local-sidecar.mjs +125 -59
- package/src/server/security.mjs +89 -4
- package/src/server/server.mjs +4 -2
- package/src/skills/steward.mjs +780 -0
- package/src/support/feedback-report.mjs +4 -3
- package/src/ui/presentation/browser.mjs +278 -0
- package/src/ui/presentation/contract.mjs +116 -0
- package/src/ui/presentation/index.mjs +7 -0
- package/src/ui/presentation/native.mjs +153 -0
- package/src/ui/presentation/proof.mjs +57 -0
- package/src/ui/presentation/schema-check.mjs +30 -0
- package/src/ui/presentation/schema.generated.mjs +1371 -0
- package/src/ui/presentation/state.mjs +43 -0
- package/src/ui/presentation/styles.mjs +67 -0
- package/src/ui/presentation/tokens.mjs +74 -0
- package/src/ui/presentation/web.mjs +85 -0
- package/src/ui/review-page.mjs +32 -0
- package/src/upgrade/explanation.mjs +33 -0
- package/src/upgrade/provenance.mjs +338 -0
- package/src/upgrade/transaction-files.mjs +103 -0
- package/src/upgrade/transaction.mjs +475 -0
- package/src/upgrade/upgrade.mjs +65 -29
- package/src/vault/hosts.mjs +21 -0
- package/src/vault/identity.mjs +19 -0
- package/src/vault/index.mjs +7 -0
- package/src/vault/interface.mjs +11 -0
- package/src/vault/metadata.mjs +31 -0
- package/src/vault/privacy.mjs +148 -0
- package/src/vault/probe.mjs +89 -0
- package/src/vault/service.mjs +131 -0
- package/src/vault/source.mjs +45 -0
- package/src/vault/storage.mjs +23 -0
- package/templates/distribution-workspace/.claude/launch.json +6 -2
- package/templates/distribution-workspace/gitignore +1 -0
- package/templates/distribution-workspace/project/README.md +2 -2
- package/templates/external-project-workspace/README.md +45 -0
- package/templates/external-project-workspace/answers.example.json +4 -0
- package/templates/external-project-workspace/atelier.project.json +40 -0
- package/templates/external-project-workspace/boundary-policy.v1.json +60 -0
- package/templates/external-project-workspace/gitignore +8 -0
- package/templates/external-project-workspace/pack-lifecycle.json +14 -0
- package/templates/external-project-workspace/packs/protocols/contract-gate.v1.json +109 -0
- package/templates/external-project-workspace/packs/sample-pack.v1.json +48 -0
- package/templates/external-project-workspace/repo-access.v1.json +9 -0
- package/templates/external-project-workspace/source/README.md +11 -0
- package/templates/launch.json +6 -2
- package/templates/private-domain-workspace/.claude/launch.json +6 -2
- package/templates/private-domain-workspace/domain/README.md +2 -2
- package/templates/private-domain-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/.claude/launch.json +6 -2
- package/templates/shared-project-workspace/gitignore +1 -0
- package/templates/shared-project-workspace/project/README.md +2 -2
package/src/graph/graph.mjs
CHANGED
|
@@ -1,261 +1,182 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import { commandProject, gitRemoteUrl, loadRepoAccess,
|
|
4
|
-
import {
|
|
3
|
+
import { commandProject, gitRemoteUrl, loadRepoAccess, remoteHost, writeJson } from '../project/config.mjs'
|
|
4
|
+
import { VALID_AUDIENCES as PROJECTION_AUDIENCES } from '../projection/policy.mjs'
|
|
5
|
+
import {
|
|
6
|
+
REPO_ACCESS_SCHEMA,
|
|
7
|
+
VALID_RELATION_TYPES,
|
|
8
|
+
buildKnowledgeGraph,
|
|
9
|
+
createGraphFileCache,
|
|
10
|
+
parseYamlSubset,
|
|
11
|
+
} from './knowledge-graph.mjs'
|
|
12
|
+
|
|
13
|
+
export { createGraphFileCache }
|
|
14
|
+
|
|
15
|
+
// Compatibility exports. Parsing, census, validation, and diagnostics all live
|
|
16
|
+
// in knowledge-graph.mjs; this module only projects its canonical result into
|
|
17
|
+
// the long-standing `atelier graph` artifact shape.
|
|
18
|
+
export const VALID_AUDIENCES = PROJECTION_AUDIENCES
|
|
19
|
+
export const VALID_RELATIONS = VALID_RELATION_TYPES
|
|
20
|
+
export const parseFrontmatterYaml = parseYamlSubset
|
|
21
|
+
|
|
22
|
+
function canonicalInput(project) {
|
|
23
|
+
const loadedAccess = loadRepoAccess(project)
|
|
24
|
+
const managed = []
|
|
25
|
+
const missing = []
|
|
26
|
+
const accessRepos = {}
|
|
5
27
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const slug = (value) => String(value ?? '').toLowerCase().replace(/\.[^.]+$/, '').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
|
|
12
|
-
const relPath = (root, file) => path.relative(root, file).split(path.sep).join('/')
|
|
13
|
-
|
|
14
|
-
// Sidecar-first census. Document extensions are always sources; any other
|
|
15
|
-
// file — json, yaml, csv, binary assets — opts in by carrying an adjacent
|
|
16
|
-
// .kg.json sidecar. The sidecar itself is metadata, never a source asset.
|
|
17
|
-
//
|
|
18
|
-
// Membership asks whether the sidecar is VISIBLE, not whether it happens to
|
|
19
|
-
// exist on this disk. A git-ignored sidecar is machine-local: obeying one
|
|
20
|
-
// enrolls a node that no tracked file declares — audience and all — so a
|
|
21
|
-
// clean checkout builds a different graph than the machine that wrote it.
|
|
22
|
-
function isSourceFile(name, sidecarVisible) {
|
|
23
|
-
if (name.endsWith('.kg.json')) return false
|
|
24
|
-
if (DOC_EXTS.has(path.extname(name).toLowerCase())) return true
|
|
25
|
-
return sidecarVisible === true
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function sidecarIsVisible(abs, root, isIgnored) {
|
|
29
|
-
const sidecar = sidecarFor(abs)
|
|
30
|
-
return fs.existsSync(sidecar) && !isIgnored(relPath(root, sidecar))
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function walk(dir, root, acc = [], isIgnored = gitIgnoreFilter(root)) {
|
|
34
|
-
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
35
|
-
if (ent.name.startsWith('.') || SKIP_DIRS.has(ent.name)) continue
|
|
36
|
-
const abs = path.join(dir, ent.name)
|
|
37
|
-
if (isIgnored(relPath(root, abs))) continue
|
|
38
|
-
if (ent.isDirectory()) walk(abs, root, acc, isIgnored)
|
|
39
|
-
if (!ent.isFile() || ent.name.endsWith('.kg.json')) continue
|
|
40
|
-
const ext = path.extname(ent.name).toLowerCase()
|
|
41
|
-
// Markdown carries its own front matter and never consults a sidecar.
|
|
42
|
-
const sidecarVisible = ext === '.md' ? false : sidecarIsVisible(abs, root, isIgnored)
|
|
43
|
-
if (isSourceFile(ent.name, sidecarVisible)) acc.push({ abs, rel: relPath(root, abs), ext, sidecarVisible })
|
|
44
|
-
}
|
|
45
|
-
return acc
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function splitFrontmatter(raw) {
|
|
49
|
-
if (!raw.startsWith('---\n')) return null
|
|
50
|
-
const end = raw.indexOf('\n---', 4)
|
|
51
|
-
if (end === -1) return null
|
|
52
|
-
return raw.slice(4, end)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function parseValue(value) {
|
|
56
|
-
const text = String(value ?? '').trim()
|
|
57
|
-
if (!text) return ''
|
|
58
|
-
if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) return text.slice(1, -1)
|
|
59
|
-
if (text === 'true') return true
|
|
60
|
-
if (text === 'false') return false
|
|
61
|
-
return text
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function setPath(target, keys, value) {
|
|
65
|
-
let cursor = target
|
|
66
|
-
for (const key of keys.slice(0, -1)) {
|
|
67
|
-
cursor[key] ??= {}
|
|
68
|
-
cursor = cursor[key]
|
|
69
|
-
}
|
|
70
|
-
cursor[keys.at(-1)] = value
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function parseFrontmatterYaml(yaml) {
|
|
74
|
-
const out = {}
|
|
75
|
-
const lines = String(yaml ?? '').split('\n')
|
|
76
|
-
const stack = [{ indent: -1, value: out, key: null }]
|
|
77
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
78
|
-
const raw = lines[index]
|
|
79
|
-
if (!raw.trim() || raw.trim().startsWith('#')) continue
|
|
80
|
-
const indent = raw.match(/^\s*/)[0].length
|
|
81
|
-
const line = raw.trim()
|
|
82
|
-
while (stack.length > 1 && indent <= stack.at(-1).indent) stack.pop()
|
|
83
|
-
const parent = stack.at(-1).value
|
|
84
|
-
if (line.startsWith('- ')) {
|
|
85
|
-
if (!Array.isArray(parent)) continue
|
|
86
|
-
parent.push(parseValue(line.slice(2)))
|
|
28
|
+
for (const repo of project.repos ?? []) {
|
|
29
|
+
if (repo.external) continue
|
|
30
|
+
if (!repo.name || !repo.path || !fs.existsSync(repo.path)) {
|
|
31
|
+
missing.push(`configured repo is missing or unreadable: ${repo.name ?? '(unnamed)'}`)
|
|
87
32
|
continue
|
|
88
33
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
parent[key] = nextYamlContainer(lines, index, indent)
|
|
94
|
-
stack.push({ indent, value: parent[key], key })
|
|
95
|
-
} else if (rest === '[]') {
|
|
96
|
-
parent[key] = []
|
|
97
|
-
stack.push({ indent, value: parent[key], key })
|
|
98
|
-
} else {
|
|
99
|
-
parent[key] = parseValue(rest)
|
|
34
|
+
managed.push({ name: repo.name, path: repo.path })
|
|
35
|
+
accessRepos[repo.name] = {
|
|
36
|
+
readBoundary:
|
|
37
|
+
loadedAccess.repos?.[repo.name]?.readBoundary ?? repo.readBoundary ?? loadedAccess.defaultReadBoundary ?? 'team',
|
|
100
38
|
}
|
|
101
39
|
}
|
|
102
|
-
return out
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function nextYamlContainer(lines, fromIndex, indent) {
|
|
106
|
-
for (let index = fromIndex + 1; index < lines.length; index += 1) {
|
|
107
|
-
const raw = lines[index]
|
|
108
|
-
if (!raw.trim() || raw.trim().startsWith('#')) continue
|
|
109
|
-
const nextIndent = raw.match(/^\s*/)[0].length
|
|
110
|
-
if (nextIndent <= indent) return {}
|
|
111
|
-
return raw.trim().startsWith('- ') ? [] : {}
|
|
112
|
-
}
|
|
113
|
-
return {}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function normalizeRelations(relations) {
|
|
117
|
-
const out = {}
|
|
118
|
-
if (!relations || typeof relations !== 'object') return out
|
|
119
|
-
for (const [type, targets] of Object.entries(relations)) {
|
|
120
|
-
if (Array.isArray(targets)) out[type] = targets.map(String)
|
|
121
|
-
else if (typeof targets === 'string') out[type] = [targets]
|
|
122
|
-
else out[type] = []
|
|
123
|
-
}
|
|
124
|
-
return out
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function sidecarFor(file) {
|
|
128
|
-
return `${file}.kg.json`
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function validateSidecar(meta, repo, file, errors) {
|
|
132
|
-
const allowed = new Set(['assetFilename', 'title', 'summary', 'tags', 'kg'])
|
|
133
|
-
const kgAllowed = new Set(['id', 'title', 'type', 'domain', 'lifecycle', 'status', 'audience', 'relations'])
|
|
134
|
-
for (const key of Object.keys(meta || {})) {
|
|
135
|
-
if (!allowed.has(key)) errors.push(`${repo.name}/${file.rel}: sidecar has unknown key ${key}`)
|
|
136
|
-
}
|
|
137
|
-
for (const key of Object.keys(meta?.kg || {})) {
|
|
138
|
-
if (!kgAllowed.has(key)) errors.push(`${repo.name}/${file.rel}: sidecar kg has unknown key ${key}`)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
40
|
|
|
142
|
-
function nodeFromMarkdown(repo, file, errors) {
|
|
143
|
-
const raw = fs.readFileSync(file.abs, 'utf8')
|
|
144
|
-
const fm = splitFrontmatter(raw)
|
|
145
|
-
if (!fm) return null
|
|
146
|
-
const parsed = parseFrontmatterYaml(fm)
|
|
147
|
-
const kg = parsed.kg ?? {}
|
|
148
|
-
if (!kg.id) errors.push(`${repo.name}/${file.rel}: kg.id is required`)
|
|
149
|
-
if (!kg.audience) errors.push(`${repo.name}/${file.rel}: kg.audience is required`)
|
|
150
|
-
if (kg.visibility) errors.push(`${repo.name}/${file.rel}: kg.visibility is invalid; use kg.audience`)
|
|
151
41
|
return {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
42
|
+
missing,
|
|
43
|
+
options: {
|
|
44
|
+
workspaceRoot: project.workspaceRoot,
|
|
45
|
+
repoEntries: managed,
|
|
46
|
+
repoAccessConfig: {
|
|
47
|
+
schema: REPO_ACCESS_SCHEMA,
|
|
48
|
+
defaultReadBoundary: loadedAccess.defaultReadBoundary ?? 'team',
|
|
49
|
+
repos: accessRepos,
|
|
50
|
+
},
|
|
51
|
+
repoAccessConfigPath: project.repoAccessPath,
|
|
52
|
+
externalRelationPrefixes: Array.isArray(project.config.graph?.externalRelationPrefixes)
|
|
53
|
+
? project.config.graph.externalRelationPrefixes
|
|
54
|
+
: [],
|
|
55
|
+
externalRelationIds: Array.isArray(project.config.graph?.externalRelationIds) ? project.config.graph.externalRelationIds : [],
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function compatibilityError(message) {
|
|
61
|
+
return String(message)
|
|
62
|
+
.replace(': missing non-Markdown sidecar ', ': non-Markdown source requires sidecar ')
|
|
63
|
+
.replace(/: active orphan sidecar has no adjacent source .+$/, ': sidecar has no matching source asset')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function compatibilityNode(node) {
|
|
67
|
+
return {
|
|
68
|
+
id: node.id,
|
|
69
|
+
title: node.title,
|
|
70
|
+
summary: node.summary,
|
|
71
|
+
repo: node.repo,
|
|
72
|
+
path: node.path,
|
|
73
|
+
type: node.kgType,
|
|
74
|
+
status: node.status,
|
|
75
|
+
audience: node.audience,
|
|
76
|
+
...(node.classification === 'unclassified'
|
|
77
|
+
? { classification: 'unclassified', classificationReason: node.classificationReason }
|
|
78
|
+
: {}),
|
|
79
|
+
tags: node.tags,
|
|
80
|
+
relations: node.relations,
|
|
81
|
+
repoAccess: node.repoAccess,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function compatibilityDiagnostic(diagnostic) {
|
|
86
|
+
if (diagnostic.code === 'unclassified-content') return diagnostic
|
|
87
|
+
return {
|
|
88
|
+
severity: diagnostic.severity,
|
|
89
|
+
code: 'audience-wider-repo-boundary',
|
|
90
|
+
node: diagnostic.node,
|
|
91
|
+
message: diagnostic.message,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function externalRepos(project) {
|
|
96
|
+
return (project.repos ?? [])
|
|
97
|
+
.filter((repo) => repo.external)
|
|
98
|
+
.map((repo) => ({
|
|
99
|
+
name: repo.name,
|
|
100
|
+
path: repo.path ?? null,
|
|
101
|
+
remoteHost: remoteHost(gitRemoteUrl(repo.path)),
|
|
102
|
+
}))
|
|
103
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function canonicalBuild(project, overrides = {}) {
|
|
107
|
+
const input = canonicalInput(project)
|
|
108
|
+
const result = buildKnowledgeGraph({ ...input.options, ...overrides })
|
|
109
|
+
return { input, result }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The supported project-to-options seam: the exact buildKnowledgeGraph options
|
|
113
|
+
// every graph consumer derives from a loaded project, plus the configured
|
|
114
|
+
// repositories that could not be read.
|
|
115
|
+
export function canonicalGraphOptions(project) {
|
|
116
|
+
return canonicalInput(project)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const canonicalEdgeId = (edge) => JSON.stringify([edge.source, edge.type, edge.target])
|
|
120
|
+
|
|
121
|
+
// The canonical graph for a loaded project, for consumers that need derived
|
|
122
|
+
// edges and the source offsets behind them. Nothing is resolved here: nodes,
|
|
123
|
+
// edges, link occurrences and link findings all come from
|
|
124
|
+
// buildKnowledgeGraph. Each edge gains a stable id and an origin; a derived
|
|
125
|
+
// edge carries every source occurrence that produced it. Embedded assets are
|
|
126
|
+
// reported beside the graph, like link occurrences: `embeds` holds one record
|
|
127
|
+
// per embed occurrence and `assets` each embedded file once. Neither adds a
|
|
128
|
+
// node or an edge.
|
|
129
|
+
// `fileCache` (createGraphFileCache) lets repeated builds of one workspace
|
|
130
|
+
// reuse the per-file census and link scan of every Markdown source whose
|
|
131
|
+
// bytes did not change; the result is the same as without it. `observedDigest`
|
|
132
|
+
// (see createGraphFileCache) lets a caller that observes sources by digest
|
|
133
|
+
// spare the read of a source it knows unchanged.
|
|
134
|
+
export function buildCanonicalGraph(project, { isLinkTargetEligible, isAssetEligible, fileCache = null, observedDigest = null } = {}) {
|
|
135
|
+
const { input, result } = canonicalBuild(project, {
|
|
136
|
+
...(isLinkTargetEligible ? { isLinkTargetEligible } : {}),
|
|
137
|
+
...(isAssetEligible ? { isAssetEligible } : {}),
|
|
138
|
+
...(fileCache ? { fileCache } : {}),
|
|
139
|
+
...(observedDigest ? { observedDigest } : {}),
|
|
140
|
+
})
|
|
141
|
+
const canonical = result.workspaceGraph ?? { nodes: [], edges: [], diagnostics: [] }
|
|
142
|
+
const occurrences = new Map()
|
|
143
|
+
for (const link of result.resolvedLinks ?? []) {
|
|
144
|
+
const id = canonicalEdgeId(link)
|
|
145
|
+
if (!occurrences.has(id)) occurrences.set(id, [])
|
|
146
|
+
occurrences.get(id).push(link)
|
|
147
|
+
}
|
|
148
|
+
const edges = canonical.edges.map((edge) => {
|
|
149
|
+
const id = canonicalEdgeId(edge)
|
|
150
|
+
return edge.declared === true
|
|
151
|
+
? { id, ...edge, origin: 'declared' }
|
|
152
|
+
: { id, ...edge, origin: 'ordinary-link', occurrences: occurrences.get(id) ?? [] }
|
|
153
|
+
})
|
|
154
|
+
const embeds = result.resolvedEmbeds ?? []
|
|
155
|
+
const assets = [...new Map(embeds.map((embed) => [embed.asset.id, embed.asset])).values()].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
|
182
156
|
return {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
157
|
+
ok: result.ok && input.missing.length === 0,
|
|
158
|
+
errors: [...input.missing, ...result.errors],
|
|
159
|
+
options: input.options,
|
|
160
|
+
nodes: canonical.nodes,
|
|
161
|
+
edges,
|
|
162
|
+
diagnostics: canonical.diagnostics,
|
|
163
|
+
links: result.resolvedLinks ?? [],
|
|
164
|
+
embeds,
|
|
165
|
+
assets,
|
|
166
|
+
linkDiagnostics: result.linkDiagnostics ?? [],
|
|
167
|
+
...(result.fileCensus ? { fileCensus: result.fileCensus } : {}),
|
|
193
168
|
}
|
|
194
169
|
}
|
|
195
170
|
|
|
196
171
|
export function buildGraph(project) {
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
for (const repo of project.repos) {
|
|
207
|
-
if (repo.external) {
|
|
208
|
-
// Identity acknowledged, nothing walked: the workspace does not own this
|
|
209
|
-
// content and cannot answer for its metadata.
|
|
210
|
-
external.push({ name: repo.name, path: repo.path ?? null, remoteHost: remoteHost(gitRemoteUrl(repo.path)) })
|
|
211
|
-
continue
|
|
212
|
-
}
|
|
213
|
-
if (!repo.name || !repo.path || !fs.existsSync(repo.path)) {
|
|
214
|
-
errors.push(`configured repo is missing or unreadable: ${repo.name ?? '(unnamed)'}`)
|
|
215
|
-
continue
|
|
216
|
-
}
|
|
217
|
-
// One batched ignore lookup per repo, shared by every walk below.
|
|
218
|
-
const isIgnored = gitIgnoreFilter(repo.path)
|
|
219
|
-
for (const file of walk(repo.path, repo.path, [], isIgnored)) {
|
|
220
|
-
// Markdown is the one inline metadata format; every other source is
|
|
221
|
-
// sidecar-described and its own bytes are never read.
|
|
222
|
-
const node = file.ext === '.md' ? nodeFromMarkdown(repo, file, errors) : nodeFromSidecar(repo, file, errors)
|
|
223
|
-
if (!node) continue
|
|
224
|
-
const boundary = repoAccess.repos?.[repo.name]?.readBoundary ?? repo.readBoundary ?? repoAccess.defaultReadBoundary ?? 'team'
|
|
225
|
-
node.repoAccess = { readBoundary: boundary }
|
|
226
|
-
if (['private', 'sensitive'].includes(node.audience) && boundary !== 'private') {
|
|
227
|
-
diagnostics.push({
|
|
228
|
-
severity: 'warning',
|
|
229
|
-
code: 'audience-wider-repo-boundary',
|
|
230
|
-
node: node.id,
|
|
231
|
-
message: `${node.id}: audience ${node.audience} is in a ${boundary}-readable repo`,
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
nodes.push(node)
|
|
235
|
-
}
|
|
236
|
-
for (const orphan of findOrphanSidecars(repo.path, isIgnored)) {
|
|
237
|
-
errors.push(`${repo.name}/${orphan}: sidecar has no matching source asset`)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const ids = new Set()
|
|
242
|
-
for (const node of nodes) {
|
|
243
|
-
if (ids.has(node.id)) errors.push(`duplicate kg.id: ${node.id}`)
|
|
244
|
-
ids.add(node.id)
|
|
245
|
-
if (!VALID_AUDIENCES.has(node.audience)) errors.push(`${node.id}: invalid audience ${node.audience}`)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
for (const node of nodes) {
|
|
249
|
-
for (const [type, targets] of Object.entries(node.relations ?? {})) {
|
|
250
|
-
if (!VALID_RELATIONS.has(type)) errors.push(`${node.id}: invalid relation type ${type}`)
|
|
251
|
-
for (const target of targets) {
|
|
252
|
-
if (!ids.has(target) && !isExternalRelationTarget(target, { externalRelationPrefixes, externalRelationIds })) {
|
|
253
|
-
errors.push(`${node.id}: relation target not found: ${target}`)
|
|
254
|
-
}
|
|
255
|
-
edges.push({ source: node.id, target, type, declared: true })
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
172
|
+
const { input, result } = canonicalBuild(project)
|
|
173
|
+
const canonical = result.workspaceGraph ?? { nodes: [], edges: [], diagnostics: [] }
|
|
174
|
+
const nodes = canonical.nodes.map(compatibilityNode)
|
|
175
|
+
// The legacy artifact exposes declared relations only. Derived Markdown-link
|
|
176
|
+
// edges remain available from buildKnowledgeGraph without changing this API.
|
|
177
|
+
const edges = canonical.edges.filter((edge) => edge.declared === true)
|
|
178
|
+
const diagnostics = canonical.diagnostics.map(compatibilityDiagnostic)
|
|
179
|
+
const errors = [...input.missing, ...result.errors.map(compatibilityError)]
|
|
259
180
|
|
|
260
181
|
return {
|
|
261
182
|
schema: 'mnstry.atelier-knowledge-graph@v1',
|
|
@@ -265,83 +186,22 @@ export function buildGraph(project) {
|
|
|
265
186
|
nodes,
|
|
266
187
|
edges,
|
|
267
188
|
diagnostics,
|
|
268
|
-
external:
|
|
189
|
+
external: externalRepos(project),
|
|
269
190
|
errors,
|
|
270
191
|
}
|
|
271
192
|
}
|
|
272
193
|
|
|
273
|
-
|
|
274
|
-
if (externalRelationIds.has(target)) return true
|
|
275
|
-
const prefix = String(target).split(':')[0]
|
|
276
|
-
return externalRelationPrefixes.has(prefix)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function findOrphanSidecars(root, isIgnored = gitIgnoreFilter(root)) {
|
|
280
|
-
const orphans = []
|
|
281
|
-
function visit(dir) {
|
|
282
|
-
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
283
|
-
if (ent.name.startsWith('.') || SKIP_DIRS.has(ent.name)) continue
|
|
284
|
-
const abs = path.join(dir, ent.name)
|
|
285
|
-
if (isIgnored(relPath(root, abs))) continue
|
|
286
|
-
if (ent.isDirectory()) visit(abs)
|
|
287
|
-
else if (ent.isFile() && ent.name.endsWith('.kg.json')) {
|
|
288
|
-
const asset = abs.slice(0, -'.kg.json'.length)
|
|
289
|
-
if (!fs.existsSync(asset)) orphans.push(relPath(root, abs))
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
visit(root)
|
|
294
|
-
return orphans
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Sidecars that exist on disk but are git-ignored, next to a file the census
|
|
298
|
-
// can see. Refused, never obeyed — and never silently: dropping them without a
|
|
299
|
-
// word is what let an untracked sidecar mint a public node.
|
|
300
|
-
function findIgnoredSidecars(root, isIgnored = gitIgnoreFilter(root)) {
|
|
301
|
-
const found = []
|
|
302
|
-
function visit(dir) {
|
|
303
|
-
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
304
|
-
if (ent.name.startsWith('.') || SKIP_DIRS.has(ent.name)) continue
|
|
305
|
-
const abs = path.join(dir, ent.name)
|
|
306
|
-
const rel = relPath(root, abs)
|
|
307
|
-
if (ent.isDirectory()) {
|
|
308
|
-
if (!isIgnored(rel)) visit(abs)
|
|
309
|
-
continue
|
|
310
|
-
}
|
|
311
|
-
if (!ent.isFile() || !ent.name.endsWith('.kg.json') || !isIgnored(rel)) continue
|
|
312
|
-
const assetRel = rel.slice(0, -'.kg.json'.length)
|
|
313
|
-
// Nothing was refused if the asset is itself machine-local or absent, and
|
|
314
|
-
// Markdown never reads a sidecar in the first place.
|
|
315
|
-
if (assetRel.toLowerCase().endsWith('.md')) continue
|
|
316
|
-
if (isIgnored(assetRel) || !fs.existsSync(path.join(root, assetRel))) continue
|
|
317
|
-
found.push({ path: assetRel, sidecar: rel })
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
visit(root)
|
|
321
|
-
return found
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Deliberately NOT part of buildGraph's return value: that object is written
|
|
325
|
-
// verbatim to the graph artifact, so a machine-local observation inside it
|
|
326
|
-
// would make the artifact differ between a developer's tree and a clean
|
|
327
|
-
// checkout — the exact churn the ignore filter exists to prevent. The operator
|
|
328
|
-
// hears about the refusal; the shared artifact never does.
|
|
194
|
+
// Machine-local ignored-sidecar observations stay outside generated artifacts.
|
|
329
195
|
export function ignoredSidecarWarnings(project) {
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
sidecar: found.sidecar,
|
|
340
|
-
message: `${repo.name}/${found.sidecar}: sidecar is git-ignored and was refused; it cannot enroll ${found.path} or describe it`,
|
|
341
|
-
})
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return warnings
|
|
196
|
+
const { result } = canonicalBuild(project)
|
|
197
|
+
return (result.ignoredSidecars ?? []).map((found) => ({
|
|
198
|
+
severity: 'warning',
|
|
199
|
+
code: 'ignored-sidecar',
|
|
200
|
+
repo: found.repo,
|
|
201
|
+
path: found.path,
|
|
202
|
+
sidecar: found.sidecar,
|
|
203
|
+
message: `${found.repo}/${found.sidecar}: sidecar is git-ignored and was refused; it cannot enroll ${found.path} or describe it`,
|
|
204
|
+
}))
|
|
345
205
|
}
|
|
346
206
|
|
|
347
207
|
export function runGraphCommand(argv = process.argv.slice(2)) {
|