@tiinex/app 0.1.1
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/LICENSE +201 -0
- package/NOTICE +31 -0
- package/README.md +26 -0
- package/docs/CONSUMER-CONTRACT.md +65 -0
- package/package.json +94 -0
- package/src/acceptance/knownScenarios.case.mjs +48 -0
- package/src/acceptance/knownScenarios.js +89 -0
- package/src/acceptance/postV423CanonicalTransitionProductVerticalSlice.case.mjs +774 -0
- package/src/acceptance/postV437M0ETimePortalProductParityClosure.case.mjs +51 -0
- package/src/acceptance/postV447M0FGithubSocialPublicationProductIntegration.case.mjs +33 -0
- package/src/acceptance/postV450M0ProductAcceptanceHardening.case.mjs +82 -0
- package/src/acceptance/postV470SchemaReadingContractMaterializationIdentitySourceCoalescingAuthorityCorrection.case.mjs +236 -0
- package/src/acceptance/recoveryAcceptance.case.mjs +196 -0
- package/src/adapters/adapter.definition.schema.json +33 -0
- package/src/adapters/archive/archive.adapter.case.mjs +212 -0
- package/src/adapters/export/export.adapter.js +79 -0
- package/src/adapters/git-native/git-native.adapter.js +43 -0
- package/src/adapters/github/github.adapter.case.mjs +843 -0
- package/src/adapters/github/github.adapter.js +395 -0
- package/src/adapters/github/github.issueEmbedded.js +262 -0
- package/src/adapters/github/github.issueMirror.js +309 -0
- package/src/adapters/github/github.issueSnapshot.js +475 -0
- package/src/adapters/github/github.issueSurface.js +188 -0
- package/src/adapters/github/github.issueTransport.js +237 -0
- package/src/adapters/github/github.publicationRead.js +24 -0
- package/src/adapters/github/github.readTransport.js +18 -0
- package/src/adapters/github/github.repoDiscovery.js +222 -0
- package/src/adapters/github/github.repoMirror.js +255 -0
- package/src/adapters/github/github.repoProxy.js +276 -0
- package/src/adapters/github/github.repoSurface.js +60 -0
- package/src/adapters/github/github.transportDiagnostics.js +68 -0
- package/src/adapters/local/local.adapter.case.mjs +72 -0
- package/src/adapters/local/local.adapter.js +188 -0
- package/src/adapters/registry.js +35 -0
- package/src/adapters/static/static.adapter.js +84 -0
- package/src/app/App.js +1 -0
- package/src/app/TiinexApp.jsx +822 -0
- package/src/app/appShell.views.jsx +58 -0
- package/src/app/artifactAuthoringCapability.js +49 -0
- package/src/app/boot/bootApp.js +19 -0
- package/src/app/boot/environment.js +1 -0
- package/src/app/canonicalAuthoringValues.js +4 -0
- package/src/app/canonicalCreationProductController.js +57 -0
- package/src/app/canonicalCreationProductSettlement.case.mjs +10 -0
- package/src/app/canonicalCreationProductSettlement.js +11 -0
- package/src/app/canonicalReferenceLocalCreateCommand.js +210 -0
- package/src/app/canonicalReferenceTargets.js +73 -0
- package/src/app/canonicalTransitionLocalCreateCommand.js +217 -0
- package/src/app/defaultWorkspaceStart.case.mjs +39 -0
- package/src/app/defaultWorkspaceStart.js +65 -0
- package/src/app/defaultWorkspaceStartCommand.js +55 -0
- package/src/app/durableLocalMutationPolicy.js +21 -0
- package/src/app/githubMaterializationSummary.js +69 -0
- package/src/app/githubProgress.js +25 -0
- package/src/app/githubSourceInput.js +47 -0
- package/src/app/githubSourceMaterializationCommand.js +97 -0
- package/src/app/githubSourceOperation.js +226 -0
- package/src/app/governanceDialogData.js +27 -0
- package/src/app/handoffPackageImportCommand.js +182 -0
- package/src/app/historyAuthority.js +53 -0
- package/src/app/initialWorkspaceBootstrapOperation.js +105 -0
- package/src/app/lineageCommand.js +118 -0
- package/src/app/lineageSourceRecovery.js +339 -0
- package/src/app/localDraftMutationCommand.js +174 -0
- package/src/app/localMaterialCommand.js +124 -0
- package/src/app/nodeGraphVerse.case.mjs +111 -0
- package/src/app/persistenceOwnership.js +92 -0
- package/src/app/productStateBoundary.js +7 -0
- package/src/app/providers/AppProviders.js +1 -0
- package/src/app/providers/LocaleProvider.js +1 -0
- package/src/app/providers/SchemaRegistryProvider.js +1 -0
- package/src/app/providers/WorkspaceProvider.js +1 -0
- package/src/app/publicTarget.js +251 -0
- package/src/app/publicTargetRestoreCommand.js +149 -0
- package/src/app/recordUi.cache.js +35 -0
- package/src/app/recordUi.js +11 -0
- package/src/app/routes/RouteSurface.js +1 -0
- package/src/app/routes/routes.js +1 -0
- package/src/app/runtimeState.js +54 -0
- package/src/app/schemaCurrentDeclaration.js +69 -0
- package/src/app/schemaFactoryLocalCreateCommand.js +101 -0
- package/src/app/schemaFactoryViewerProof.case.mjs +134 -0
- package/src/app/schemaFactoryViewerProof.js +20 -0
- package/src/app/schemaNavigationCommand.js +285 -0
- package/src/app/schemaNavigationMaterialization.js +171 -0
- package/src/app/schemaNavigationRuntimeCatalog.js +25 -0
- package/src/app/schemaNavigationState.js +66 -0
- package/src/app/schemaReadingContractQualification.js +91 -0
- package/src/app/schemaSourceRecovery.js +373 -0
- package/src/app/scrollPersistence.js +30 -0
- package/src/app/semanticOperationHistory.js +18 -0
- package/src/app/shareActionCommand.js +55 -0
- package/src/app/shareProjection.js +190 -0
- package/src/app/shell/AppShell.js +1 -0
- package/src/app/shell/Header.js +1 -0
- package/src/app/shell/Navigation.js +1 -0
- package/src/app/shell/SurfaceSwitcher.js +1 -0
- package/src/app/sourceTransportRefresh.js +176 -0
- package/src/app/startupOwnership.js +40 -0
- package/src/app/startupPresentation.case.mjs +34 -0
- package/src/app/startupPresentation.js +33 -0
- package/src/app/startupPresentation.views.jsx +21 -0
- package/src/app/startupRenderPhase.js +9 -0
- package/src/app/startupWorkspaceCommand.js +62 -0
- package/src/app/startupWorkspaceConfigApplication.js +49 -0
- package/src/app/statePersistenceScheduler.js +106 -0
- package/src/app/tiinexAppConfigPlan.js +108 -0
- package/src/app/tiinexAppConfigSource.js +349 -0
- package/src/app/tiinexAppStartupSource.js +79 -0
- package/src/app/tiinexHostedWorkspaceConventions.js +572 -0
- package/src/app/timePortalHistoricalRead.js +114 -0
- package/src/app/timePortalSnapshotResolution.js +53 -0
- package/src/app/urlMaterialCommand.js +24 -0
- package/src/app/useCanonicalPlacementSelectionOptions.js +9 -0
- package/src/app/useCanonicalReferenceSelectionOptions.js +8 -0
- package/src/app/useLocalMaterialIntake.js +68 -0
- package/src/app/useTimePortalProductController.jsx +111 -0
- package/src/app/useWorkspaceEntrypointIntake.js +77 -0
- package/src/app/useWorkspaceSelectionProductController.jsx +21 -0
- package/src/app/viewState.js +35 -0
- package/src/app/viewport.js +21 -0
- package/src/app/visualDormancy.js +286 -0
- package/src/app/workspaceContinuityNotices.js +32 -0
- package/src/app/workspaceDisplayCounts.js +9 -0
- package/src/app/workspaceEntrypointIntakeCommand.js +87 -0
- package/src/app/workspaceEntrypointProductRouting.case.mjs +20 -0
- package/src/app/workspaceExportCommand.js +21 -0
- package/src/app/workspaceExportDownload.js +23 -0
- package/src/app/workspaceExportExecutionCommand.js +10 -0
- package/src/app/workspaceGithubPublication.js +237 -0
- package/src/app/workspaceHandoffExport.js +56 -0
- package/src/app/workspaceHomeTarget.js +9 -0
- package/src/app/workspaceMulticolumn.js +113 -0
- package/src/app/workspaceNavigationParity.case.mjs +110 -0
- package/src/app/workspaceOpenProgress.js +26 -0
- package/src/app/workspacePlacementOptions.js +46 -0
- package/src/app/workspaceRecordActions.js +158 -0
- package/src/app/workspaceScopedInteraction.js +68 -0
- package/src/app/workspaceSelectionOriginContext.js +29 -0
- package/src/app/workspaceSelectionSession.js +54 -0
- package/src/app/workspaceSourceCloseCommand.js +25 -0
- package/src/app/workspaceSpineProductRouting.case.mjs +22 -0
- package/src/app/workspaceStartupTransition.js +32 -0
- package/src/app/workspaceWindow.js +85 -0
- package/src/artifacts/artifact.fixture.js +1 -0
- package/src/artifacts/artifact.identity.js +6 -0
- package/src/artifacts/artifact.identityAggregation.js +218 -0
- package/src/artifacts/artifact.model.js +2 -0
- package/src/artifacts/artifact.parse.case.mjs +196 -0
- package/src/artifacts/artifact.participantIndex.js +189 -0
- package/src/artifacts/fixtures/evidence.trace.md +50 -0
- package/src/artifacts/fixtures/topic.trace.md +33 -0
- package/src/artifacts/fixtures/unknown-schema.trace.md +25 -0
- package/src/audit/audit.findings.js +1 -0
- package/src/audit/audit.model.js +2 -0
- package/src/audit/audit.plan.js +13 -0
- package/src/audit/audit.report.js +12 -0
- package/src/audit/audit.severity.js +1 -0
- package/src/audit/audit.traverse.js +119 -0
- package/src/audit/integrity/auditIntegrity.checks.js +1 -0
- package/src/audit/integrity/auditIntegrity.compare.js +1 -0
- package/src/audit/lineage/auditLineage.load.js +1 -0
- package/src/audit/lineage/auditLineage.plan.js +1 -0
- package/src/audit/lineage/auditLineage.resolve.js +12 -0
- package/src/audit/sources/auditSources.boundaries.js +1 -0
- package/src/audit/sources/auditSources.checks.js +1 -0
- package/src/audit/validation/auditValidation.recheck.js +1 -0
- package/src/audit/validation/auditValidation.run.js +1 -0
- package/src/build.identity.js +15 -0
- package/src/commands/command.vocabulary.js +14 -0
- package/src/conformance/conformance.fixtures.js +151 -0
- package/src/conformance/conformance.run.js +238 -0
- package/src/data/applicationDataStore.js +23 -0
- package/src/data/companionAccess.js +35 -0
- package/src/diagnostics/events.js +1 -0
- package/src/diagnostics/invariant.js +1 -0
- package/src/diagnostics/schemaBindingChecks.js +1 -0
- package/src/diagnostics/schemaModuleChecks.js +1 -0
- package/src/diagnostics/sourceBoundaryChecks.js +5 -0
- package/src/diagnostics/sourceTransport.report.js +190 -0
- package/src/discovery/discovery.controls.js +7 -0
- package/src/export/export.plan.js +182 -0
- package/src/export/handoff.plan.js +50 -0
- package/src/export/package.builder.case.mjs +97 -0
- package/src/export/package.controlTopology.case.mjs +130 -0
- package/src/export/package.zip.case.mjs +33 -0
- package/src/graph/artifactGraph.model.js +344 -0
- package/src/graph/artifactGraph.project.js +139 -0
- package/src/graph/artifactGraph.worker.js +10 -0
- package/src/i18n/i18n.keys.js +1 -0
- package/src/i18n/i18n.provider.js +1 -0
- package/src/i18n/i18n.resolve.js +5 -0
- package/src/i18n/locales/en/common.json +4 -0
- package/src/i18n/locales/en/schemas/evidence.json +3 -0
- package/src/i18n/locales/en/schemas/presentation-surface.json +3 -0
- package/src/i18n/locales/en/schemas/preservation.json +3 -0
- package/src/i18n/locales/en/schemas/root.json +3 -0
- package/src/i18n/locales/en/schemas/schema-module.json +3 -0
- package/src/i18n/locales/en/schemas/topic.json +3 -0
- package/src/i18n/locales/en/surfaces/audit.json +3 -0
- package/src/i18n/locales/en/surfaces/create.json +3 -0
- package/src/i18n/locales/en/surfaces/detail.json +3 -0
- package/src/i18n/locales/en/surfaces/display-options.json +3 -0
- package/src/i18n/locales/en/surfaces/edit.json +3 -0
- package/src/i18n/locales/en/surfaces/feed.json +3 -0
- package/src/i18n/locales/en/surfaces/lineage.json +3 -0
- package/src/i18n/locales/en/surfaces/preview.json +3 -0
- package/src/i18n/locales/en/surfaces/share.json +3 -0
- package/src/i18n/locales/en/surfaces/source-settings.json +3 -0
- package/src/i18n/locales/en/surfaces/tree.json +3 -0
- package/src/i18n/locales/sv/common.json +4 -0
- package/src/i18n/locales/sv/schemas/evidence.json +3 -0
- package/src/i18n/locales/sv/schemas/presentation-surface.json +3 -0
- package/src/i18n/locales/sv/schemas/preservation.json +3 -0
- package/src/i18n/locales/sv/schemas/root.json +3 -0
- package/src/i18n/locales/sv/schemas/schema-module.json +3 -0
- package/src/i18n/locales/sv/schemas/topic.json +3 -0
- package/src/i18n/locales/sv/surfaces/audit.json +3 -0
- package/src/i18n/locales/sv/surfaces/create.json +3 -0
- package/src/i18n/locales/sv/surfaces/detail.json +3 -0
- package/src/i18n/locales/sv/surfaces/display-options.json +3 -0
- package/src/i18n/locales/sv/surfaces/edit.json +3 -0
- package/src/i18n/locales/sv/surfaces/feed.json +3 -0
- package/src/i18n/locales/sv/surfaces/lineage.json +3 -0
- package/src/i18n/locales/sv/surfaces/preview.json +3 -0
- package/src/i18n/locales/sv/surfaces/share.json +3 -0
- package/src/i18n/locales/sv/surfaces/source-settings.json +3 -0
- package/src/i18n/locales/sv/surfaces/tree.json +3 -0
- package/src/index.js +10 -0
- package/src/integrity/integrity.testFixture.js +9 -0
- package/src/interaction/interaction.spine.js +12 -0
- package/src/lineage/lineage.audit.js +1 -0
- package/src/lineage/lineage.edges.js +1 -0
- package/src/main.js +319 -0
- package/src/main.jsx +34 -0
- package/src/multiverse/multiverse.model.js +10 -0
- package/src/parity/poc.m1ParityScenarios.js +42 -0
- package/src/parity/poc.parityLedger.js +261 -0
- package/src/publication/publication.githubSocialContract.case.mjs +247 -0
- package/src/publication/publication.preflight.case.mjs +64 -0
- package/src/react/TiinexApplication.jsx +39 -0
- package/src/react/VerseHost.jsx +29 -0
- package/src/react/applicationDataContext.jsx +30 -0
- package/src/react.js +2 -0
- package/src/readers/modes/audit.reader.js +1 -0
- package/src/readers/modes/detail.reader.js +1 -0
- package/src/readers/modes/handover.reader.js +1 -0
- package/src/readers/modes/mobile.reader.js +1 -0
- package/src/readers/modes/print.reader.js +1 -0
- package/src/readers/modes/scan.reader.js +1 -0
- package/src/readers/reader.model.js +1 -0
- package/src/readers/reader.resolve.js +2 -0
- package/src/reingest/reingest.plan.case.mjs +65 -0
- package/src/runtime.js +54 -0
- package/src/schemas/README.md +99 -0
- package/src/schemas/coordination/handoff/tiinex.handoff.v1.schema.md +614 -0
- package/src/schemas/coordination/project/tiinex.project.v1.schema.md +311 -0
- package/src/schemas/core/decision/tiinex.decision.v1.schema.md +227 -0
- package/src/schemas/core/evidence/tiinex.evidence.v1.schema.md +318 -0
- package/src/schemas/core/feedback/tiinex.feedback.v1.schema.md +243 -0
- package/src/schemas/core/interpretation/.transitions/evidence-to-interpretation-transition-definition.trace.md +102 -0
- package/src/schemas/core/interpretation/tiinex.interpretation.v1.schema.md +308 -0
- package/src/schemas/core/preservation/tiinex.preservation.v1.schema.md +312 -0
- package/src/schemas/core/relation/.generation/reference-relation-generation-authority.trace.md +185 -0
- package/src/schemas/core/relation/.transitions/topic-references-task-transition-definition.trace.md +119 -0
- package/src/schemas/core/relation/tiinex.relation.v1.schema.md +240 -0
- package/src/schemas/core/relation/workspace/representation/tiinex.workspace.representation.v1.schema.md +550 -0
- package/src/schemas/core/signal/tiinex.signal.v1.schema.md +239 -0
- package/src/schemas/core/task/.transitions/create-task-transition-definition.trace.md +93 -0
- package/src/schemas/core/task/.transitions/topic-to-task-transition-definition.trace.md +102 -0
- package/src/schemas/core/task/task-semantic-package.trace.md +53 -0
- package/src/schemas/core/task/tiinex.task.v1-transitions.trace.md +33 -0
- package/src/schemas/core/task/tiinex.task.v1.authoring.js +65 -0
- package/src/schemas/core/task/tiinex.task.v1.schema.md +229 -0
- package/src/schemas/core/topic/.transitions/create-topic-transition-definition.trace.md +93 -0
- package/src/schemas/core/topic/tiinex.topic.v1-transitions.trace.md +33 -0
- package/src/schemas/core/topic/tiinex.topic.v1.schema.md +197 -0
- package/src/schemas/core/topic/topic-semantic-package.trace.md +53 -0
- package/src/schemas/discovery/finding/tiinex.discovery.finding.v1.schema.md +240 -0
- package/src/schemas/discovery/tiinex.discovery.v1.schema.md +294 -0
- package/src/schemas/manifest.json +367 -0
- package/src/schemas/origins.js +46 -0
- package/src/schemas/party/organization/tiinex.party.organization.v1.schema.md +290 -0
- package/src/schemas/party/role/tiinex.party.role.v1.schema.md +298 -0
- package/src/schemas/party/tiinex.party.v1.schema.js +14 -0
- package/src/schemas/party/tiinex.party.v1.schema.json +23 -0
- package/src/schemas/party/tiinex.party.v1.schema.md +311 -0
- package/src/schemas/party/tiinex.party.v1.schema.runtime.json +2033 -0
- package/src/schemas/party/tiinex.party.v1.schema.source.js +9 -0
- package/src/schemas/presentation/surface/tiinex.presentation.surface.v1.schema.md +476 -0
- package/src/schemas/reduction/tiinex.reduction.v1.schema.md +248 -0
- package/src/schemas/schema/module/tiinex.schema.module.v1.schema.md +465 -0
- package/src/schemas/schema.companionContract.case.mjs +259 -0
- package/src/schemas/schema.factory.case.mjs +256 -0
- package/src/schemas/schema.factory.viewerProjection.js +183 -0
- package/src/schemas/schemaMarkdownCatalog.js +19 -0
- package/src/schemas/tiinex.root.v1.schema.md +865 -0
- package/src/schemas/validation/finding/tiinex.validation.finding.v1.schema.md +309 -0
- package/src/schemas/validation/method/tiinex.validation.method.v1.schema.md +280 -0
- package/src/schemas/validation/report/tiinex.validation.report.v1.schema.md +300 -0
- package/src/schemas/workspace/tiinex.workspace.v1.schema.md +486 -0
- package/src/schemas/workspace/workspace.add.views.jsx +327 -0
- package/src/schemas/workspace/workspace.audit.views.jsx +160 -0
- package/src/schemas/workspace/workspace.auditBadge.views.jsx +98 -0
- package/src/schemas/workspace/workspace.canonicalReferenceDialog.views.jsx +63 -0
- package/src/schemas/workspace/workspace.canonicalTaskDialog.views.jsx +169 -0
- package/src/schemas/workspace/workspace.cardActions.js +9 -0
- package/src/schemas/workspace/workspace.cards.views.jsx +146 -0
- package/src/schemas/workspace/workspace.chrome.views.jsx +299 -0
- package/src/schemas/workspace/workspace.continuationDialog.views.jsx +80 -0
- package/src/schemas/workspace/workspace.discovery.views.jsx +63 -0
- package/src/schemas/workspace/workspace.discoveryRenderWindow.js +56 -0
- package/src/schemas/workspace/workspace.displayOptions.views.jsx +114 -0
- package/src/schemas/workspace/workspace.entrypointChoice.views.jsx +30 -0
- package/src/schemas/workspace/workspace.exportDialog.controller.jsx +40 -0
- package/src/schemas/workspace/workspace.exportDialog.views.jsx +142 -0
- package/src/schemas/workspace/workspace.governance.views.jsx +45 -0
- package/src/schemas/workspace/workspace.graph.views.jsx +134 -0
- package/src/schemas/workspace/workspace.lineage.views.jsx +307 -0
- package/src/schemas/workspace/workspace.read.views.jsx +105 -0
- package/src/schemas/workspace/workspace.recordActionButton.views.jsx +35 -0
- package/src/schemas/workspace/workspace.recordDialogs.views.jsx +257 -0
- package/src/schemas/workspace/workspace.selection.views.jsx +14 -0
- package/src/schemas/workspace/workspace.sourceReceipt.views.jsx +40 -0
- package/src/schemas/workspace/workspace.timePortal.views.jsx +56 -0
- package/src/schemas/workspace/workspace.tree.views.jsx +107 -0
- package/src/schemas/workspace/workspace.viewFormatting.js +172 -0
- package/src/schemas/workspace/workspace.views.jsx +210 -0
- package/src/source-settings/sourceSettings.model.js +7 -0
- package/src/sources/draft/draft.source.js +1 -0
- package/src/sources/github/github.loader.js +204 -0
- package/src/sources/github/github.permalink.js +1 -0
- package/src/sources/github/github.source.js +1 -0
- package/src/sources/github/github.transport.case.mjs +237 -0
- package/src/sources/github/github.transport.js +472 -0
- package/src/sources/local/local.source.js +1 -0
- package/src/sources/source.assetReferences.js +109 -0
- package/src/sources/source.boundaries.js +24 -0
- package/src/sources/source.identity.js +56 -0
- package/src/sources/source.model.case.mjs +26 -0
- package/src/sources/source.model.js +56 -0
- package/src/sources/source.presenter.js +66 -0
- package/src/sources/source.resolve.js +11 -0
- package/src/sources/static/static.source.js +1 -0
- package/src/sources/transport.policy.case.mjs +39 -0
- package/src/sources/transport.policy.js +81 -0
- package/src/storage/storage.policy.js +79 -0
- package/src/styles/app.css +8699 -0
- package/src/styles/responsive.css +10 -0
- package/src/styles/theme.css +4 -0
- package/src/styles/tokens.css +30 -0
- package/src/surfaces/audit/AuditSurface.js +1 -0
- package/src/surfaces/audit/audit.model.js +1 -0
- package/src/surfaces/audit/audit.render.js +1 -0
- package/src/surfaces/contracts.js +45 -0
- package/src/surfaces/create/CreateSurface.js +1 -0
- package/src/surfaces/create/create.model.js +1 -0
- package/src/surfaces/create/create.render.js +1 -0
- package/src/surfaces/detail/DetailSurface.js +1 -0
- package/src/surfaces/detail/detail.model.js +1 -0
- package/src/surfaces/detail/detail.render.js +1 -0
- package/src/surfaces/display-options/DisplayOptionsSurface.js +1 -0
- package/src/surfaces/display-options/displayOptions.model.js +1 -0
- package/src/surfaces/display-options/displayOptions.render.js +1 -0
- package/src/surfaces/edit/EditSurface.js +1 -0
- package/src/surfaces/edit/edit.model.js +1 -0
- package/src/surfaces/edit/edit.render.js +1 -0
- package/src/surfaces/feed/FeedSurface.js +1 -0
- package/src/surfaces/feed/feed.model.js +1 -0
- package/src/surfaces/feed/feed.render.js +1 -0
- package/src/surfaces/lineage/LineageSurface.js +1 -0
- package/src/surfaces/lineage/lineage.model.js +1 -0
- package/src/surfaces/lineage/lineage.render.js +1 -0
- package/src/surfaces/preview/PreviewSurface.js +1 -0
- package/src/surfaces/preview/preview.model.js +1 -0
- package/src/surfaces/preview/preview.render.js +1 -0
- package/src/surfaces/registry.js +58 -0
- package/src/surfaces/share/ShareSurface.js +1 -0
- package/src/surfaces/share/share.model.js +1 -0
- package/src/surfaces/share/share.render.js +1 -0
- package/src/surfaces/source-settings/SourceSettingsSurface.js +1 -0
- package/src/surfaces/source-settings/sourceSettings.model.js +1 -0
- package/src/surfaces/source-settings/sourceSettings.render.js +1 -0
- package/src/surfaces/surfaceContext.js +1 -0
- package/src/surfaces/tree/TreeSurface.js +1 -0
- package/src/surfaces/tree/tree.model.js +1 -0
- package/src/surfaces/tree/tree.render.js +1 -0
- package/src/tooling/portable/adapters/cli/cli.common-output.case.mjs +153 -0
- package/src/tooling/portable/adapters/cli/cli.handoff-sibling-allocation.case.mjs +52 -0
- package/src/tooling/portable/adapters/cli/cli.run.case.mjs +372 -0
- package/src/tooling/portable/adapters/cli/cli.summaryProjection.case.mjs +46 -0
- package/src/tooling/portable/audit/audit.capability.case.mjs +45 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/collaboration/dogfood/001-tiinex-development-dogfood-collaboration-model.trace.fixture.txt +37 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/handoff/tooling/001-v480-tooling-workflow-schema-enablement-handoff.trace.fixture.txt +108 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/handoff/tooling/002-v481-tooling-recipient-relative-handoff-material-closure-planner-foundation-handoff.trace.fixture.txt +130 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/loom/role/001-loom-role.trace.fixture.txt +61 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/sigma/role/001-1-sigma-role.trace.fixture.txt +61 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-1-1-site-tooling-v474-loaded-parent-identity-evidence-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-1-site-tooling-v474-loaded-parent-identity-evidence-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-1-site-tooling-v473-portable-parent-authority-coherence-metadata-fidelity-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-1-site-tooling-v473-portable-parent-authority-coherence-metadata-fidelity-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-1-site-tooling-v472-portable-exact-authoring-fidelity-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-1-site-tooling-v472-portable-exact-authoring-fidelity-closure.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-1-site-tooling-v471-portable-lineage-authoring-closure-result.trace.fixture.txt +48 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/001-site-tooling-v471-portable-lineage-authoring-closure.trace.fixture.txt +39 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/002-1-v475-canonical-artifact-envelope-reference-integrity-validation.trace.fixture.txt +43 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/003-1-v476-canonical-authority-binding-integrity-method-lineage-alloca.trace.fixture.txt +42 -0
- package/src/tooling/portable/fixtures/legacy-artifacts/tooling/dogfood/027-handoff-package-workspace-archive-and-control-plane-minimality-audit.trace.fixture.txt +56 -0
- package/src/tooling/portable/fixtures/legacyArtifactFixtures.mjs +34 -0
- package/src/tooling/portable/grounding/grounding.capsule.case.mjs +65 -0
- package/src/tooling/portable/grounding/grounding.readiness.case.mjs +543 -0
- package/src/tooling/portable/grounding/grounding.semanticIntegration.case.mjs +131 -0
- package/src/tooling/portable/handoff/boundedWorkspaceRepresentation.case.mjs +103 -0
- package/src/tooling/portable/handoff/carrierLineage.fixedWidth.case.mjs +97 -0
- package/src/tooling/portable/handoff/coldStartQualification.case.mjs +293 -0
- package/src/tooling/portable/handoff/fixtures/027-invalid-audit-result.fixture.txt +192 -0
- package/src/tooling/portable/handoff/fixtures/027-invalid-return-handoff.fixture.txt +92 -0
- package/src/tooling/portable/handoff/fixtures/cold-start-qualification.v1.examples.json +60 -0
- package/src/tooling/portable/handoff/handoff.manufacture.case.mjs +293 -0
- package/src/tooling/portable/handoff/materialClosure.case.mjs +727 -0
- package/src/tooling/portable/handoff/multiRootManufacture.case.mjs +615 -0
- package/src/tooling/portable/handoff/operatorBridgeCore.case.mjs +205 -0
- package/src/tooling/portable/handoff/workspaceLandingPlan.case.mjs +143 -0
- package/src/tooling/portable/input/node.input.case.mjs +293 -0
- package/src/tooling/portable/lifecycle/lifecycle.readiness.case.mjs +179 -0
- package/src/tooling/portable/lineage/fixtures/lineage.integrity.repair-projection.v1.examples.json +605 -0
- package/src/tooling/portable/lineage/lineage.integrity.shared-capability.case.mjs +37 -0
- package/src/tooling/portable/lineage/publicationProviderReceipt.fixture.mjs +50 -0
- package/src/tooling/portable/package/fixtures/reference-relation-generation-authority.trace.md +84 -0
- package/src/tooling/portable/package/fixtures/tiinex.root.v1.package-authority.contract-fixture.md +64 -0
- package/src/tooling/portable/package/fixtures/tiinex.schema.generation.v1.generation-binding.contract-fixture.md +140 -0
- package/src/tooling/portable/package/fixtures/tiinex.schema.transition.companion.v1.contract-fixture.md +73 -0
- package/src/tooling/portable/package/fixtures/tiinex.semantic.package.v1.contract-fixture.md +147 -0
- package/src/tooling/portable/package/fixtures/tiinex.transition.definition.v1.package-contract-fixture.md +77 -0
- package/src/tooling/portable/providers/schema.providers.genericity.case.mjs +31 -0
- package/src/tooling/portable/reduction/reduction.preflight.case.mjs +298 -0
- package/src/tooling/portable/schema/fixtures/evidence-preservation-body.inheritance-record-fixture.md +65 -0
- package/src/tooling/portable/schema/fixtures/tiinex.portal.time.v1.contract-fixture.md +294 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.contract-fixture.md +584 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.machine-shape.contract-fixture.md +44 -0
- package/src/tooling/portable/schema/fixtures/tiinex.root.v1.ordinary-target.contract-fixture.md +630 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.contract.v1.repeated-record.contract-fixture.md +69 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.generation.v1.repeated-record.contract-fixture.md +98 -0
- package/src/tooling/portable/schema/fixtures/tiinex.schema.inheritance.v1.repeated-record.contract-fixture.md +58 -0
- package/src/tooling/portable/schema/fixtures/tiinex.task.v1.contract-fixture.md +165 -0
- package/src/tooling/portable/schema/fixtures/tiinex.transition.definition.v1.contract-fixture.md +744 -0
- package/src/tooling/portable/schema/fixtures/tiinex.transition.definition.v1.field-domain.contract-fixture.md +319 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.relation.v1.schema.md +240 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.contract.v1.schema.md +391 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.generation.v1.schema.md +379 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.transition.companion.v1.schema.md +265 -0
- package/src/transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.semantic.package.v1.schema.md +402 -0
- package/src/transitions/canonical-schema-cache/52ecdea0a75893882ce282214d155f70e1309c2a/tiinex.topic.v1.schema.md +197 -0
- package/src/transitions/canonical-schema-cache/d69b8ff55a56b8cb9282b8684db6a938a4435b94/tiinex.root.v1.schema.md +797 -0
- package/src/transitions/canonical-schema-cache/d69b8ff55a56b8cb9282b8684db6a938a4435b94/tiinex.transition.definition.v1.schema.md +1062 -0
- package/src/transitions/canonicalTransition.packageContracts.js +22 -0
- package/src/transitions/canonicalTransition.productDefaults.js +105 -0
- package/src/transitions/canonicalTransition.schemaCache.case.mjs +45 -0
- package/src/transitions/canonicalTransition.schemaCache.js +81 -0
- package/src/transitions/canonicalTransition.semanticPackage.js +146 -0
- package/src/transitions/record.transitions.case.mjs +88 -0
- package/src/transitions/transition.authorityRepresentation.js +55 -0
- package/src/transitions/transition.availabilityPlanner.js +376 -0
- package/src/transitions/transition.availabilitySemantics.js +150 -0
- package/src/transitions/transition.definitionReadProjection.js +107 -0
- package/src/transitions/transition.definitionRegistry.js +355 -0
- package/src/transitions/transition.definitionRegistryAggregation.js +21 -0
- package/src/transitions/transition.definitions.js +46 -0
- package/src/transitions/transition.explicitGenerationPlanner.js +58 -0
- package/src/transitions/transition.generationInputPlanner.js +19 -0
- package/src/transitions/transition.interpretationMaterializer.js +2 -0
- package/src/transitions/transition.invocationBindingPacket.js +52 -0
- package/src/transitions/transition.invocationBindingPlanner.js +467 -0
- package/src/transitions/transition.legacyShorthand.js +133 -0
- package/src/transitions/transition.localArtifactMaterializers.js +16 -0
- package/src/transitions/transition.localCreateCapability.js +111 -0
- package/src/transitions/transition.outputMaterializationPlanner.js +336 -0
- package/src/transitions/transition.parentReference.js +90 -0
- package/src/transitions/transition.presentation.js +43 -0
- package/src/transitions/transition.productPreparation.js +377 -0
- package/src/transitions/transition.productPresentation.browser.js +28 -0
- package/src/transitions/transition.productPresentation.js +55 -0
- package/src/transitions/transition.referenceCreateCapability.js +119 -0
- package/src/transitions/transition.referenceTarget.js +50 -0
- package/src/transitions/transition.relationMaterializer.js +2 -0
- package/src/transitions/transition.resultSemantics.js +453 -0
- package/src/transitions/transition.taskMaterializer.js +2 -0
- package/src/transitions/transition.topicMaterializer.js +2 -0
- package/src/transitions/transition.validate.case.mjs +58 -0
- package/src/ui/cards/ArtifactCard.js +1 -0
- package/src/ui/cards/SchemaCard.js +1 -0
- package/src/ui/cards/SourceCard.js +1 -0
- package/src/ui/dialog.presenter.js +24 -0
- package/src/ui/feedback/DegradedNotice.js +1 -0
- package/src/ui/feedback/EmptyState.js +1 -0
- package/src/ui/feedback/ValidationNotice.js +1 -0
- package/src/ui/forms/FormRenderer.js +1 -0
- package/src/ui/forms/fields/MarkdownField.js +1 -0
- package/src/ui/forms/fields/SelectField.js +1 -0
- package/src/ui/forms/fields/SourcePickerField.js +1 -0
- package/src/ui/forms/fields/TextField.js +1 -0
- package/src/ui/icon.paths.js +37 -0
- package/src/ui/layout/Panel.js +1 -0
- package/src/ui/layout/ResponsiveFrame.js +1 -0
- package/src/ui/layout/SplitView.js +1 -0
- package/src/ui/layout/Stack.js +1 -0
- package/src/ui/panels/DetailPanel.js +1 -0
- package/src/ui/panels/LineagePanel.js +1 -0
- package/src/ui/panels/SourcePanel.js +1 -0
- package/src/ui/primitives/Badge.js +1 -0
- package/src/ui/primitives/Badge.jsx +5 -0
- package/src/ui/primitives/Button.js +1 -0
- package/src/ui/primitives/Button.jsx +12 -0
- package/src/ui/primitives/Card.js +1 -0
- package/src/ui/primitives/Field.js +1 -0
- package/src/ui/primitives/Field.jsx +21 -0
- package/src/ui/primitives/Icon.jsx +90 -0
- package/src/ui/primitives/Modal.jsx +27 -0
- package/src/validation/i18n.js +37 -0
- package/src/validation/validateArtifact.case.mjs +41 -0
- package/src/verses/column/ColumnVerse.js +1 -0
- package/src/verses/column/column.model.js +6 -0
- package/src/verses/column/column.project.js +7 -0
- package/src/verses/context.js +26 -0
- package/src/verses/contracts.js +1 -0
- package/src/verses/hostRegistry.js +47 -0
- package/src/verses/registry.js +22 -0
- package/src/verses/universe.model.js +8 -0
- package/src/verses/universe.presenter.js +8 -0
- package/src/verses/universe.project.js +12 -0
- package/src/viewer.js +34 -0
- package/src/workspace.model.js +5 -0
- package/src/workspaces/workspace.actions.js +1 -0
- package/src/workspaces/workspace.artifactActions.js +56 -0
- package/src/workspaces/workspace.auditView.js +153 -0
- package/src/workspaces/workspace.candidates.js +149 -0
- package/src/workspaces/workspace.config.js +153 -0
- package/src/workspaces/workspace.configuredSource.js +68 -0
- package/src/workspaces/workspace.discoveryProgress.js +34 -0
- package/src/workspaces/workspace.discoveryView.js +386 -0
- package/src/workspaces/workspace.displayFilters.js +63 -0
- package/src/workspaces/workspace.displayOptions.js +58 -0
- package/src/workspaces/workspace.emptyStateCopy.js +24 -0
- package/src/workspaces/workspace.entrypointLifecycle.js +131 -0
- package/src/workspaces/workspace.entrypointViewIntent.js +83 -0
- package/src/workspaces/workspace.entrypoints.js +157 -0
- package/src/workspaces/workspace.feedSort.js +152 -0
- package/src/workspaces/workspace.import.js +212 -0
- package/src/workspaces/workspace.importConflicts.js +200 -0
- package/src/workspaces/workspace.importLifecycle.case.mjs +103 -0
- package/src/workspaces/workspace.lifecycle.case.mjs +353 -0
- package/src/workspaces/workspace.lifecycle.js +357 -0
- package/src/workspaces/workspace.lineageView.js +244 -0
- package/src/workspaces/workspace.localSourceLifecycle.js +91 -0
- package/src/workspaces/workspace.materialLedger.js +110 -0
- package/src/workspaces/workspace.materialReconciliation.case.mjs +164 -0
- package/src/workspaces/workspace.materialReconciliation.js +390 -0
- package/src/workspaces/workspace.memberIdentity.js +140 -0
- package/src/workspaces/workspace.model.js +2 -0
- package/src/workspaces/workspace.navigation.js +20 -0
- package/src/workspaces/workspace.openSemantics.js +33 -0
- package/src/workspaces/workspace.pathTree.js +132 -0
- package/src/workspaces/workspace.persistence.case.mjs +271 -0
- package/src/workspaces/workspace.persistence.js +469 -0
- package/src/workspaces/workspace.persistenceCache.js +41 -0
- package/src/workspaces/workspace.persistenceClear.js +45 -0
- package/src/workspaces/workspace.persistencePresentation.js +93 -0
- package/src/workspaces/workspace.persistenceRecovery.js +147 -0
- package/src/workspaces/workspace.persistenceRouteCache.js +82 -0
- package/src/workspaces/workspace.publicationReceipts.case.mjs +36 -0
- package/src/workspaces/workspace.publicationReceipts.js +56 -0
- package/src/workspaces/workspace.recoverabilityView.js +264 -0
- package/src/workspaces/workspace.route.js +432 -0
- package/src/workspaces/workspace.runtimeCanonical.js +18 -0
- package/src/workspaces/workspace.sourceMaterial.js +71 -0
- package/src/workspaces/workspace.sourceRecordPath.js +5 -0
- package/src/workspaces/workspace.sourceRecords.js +75 -0
- package/src/workspaces/workspace.store.js +3 -0
- package/src/workspaces/workspace.summary.js +116 -0
- package/src/workspaces/workspace.timePortal.js +137 -0
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createRecordFromMarkdown } from '@tiinex/core/artifacts/artifact.record.js';
|
|
4
|
+
import { createPersistenceOwnershipPolicy, PersistenceRouteOwner } from '../app/persistenceOwnership.js';
|
|
5
|
+
import { executeCanonicalTransitionLocalCreate } from '../app/canonicalTransitionLocalCreateCommand.js';
|
|
6
|
+
import {
|
|
7
|
+
CANONICAL_TRANSITION_SCHEMA_CACHE_MANIFEST,
|
|
8
|
+
qualifyCanonicalTransitionSchemaCache
|
|
9
|
+
} from '../transitions/canonicalTransition.schemaCache.js';
|
|
10
|
+
import { prepareCanonicalTransitionProductActions } from '../transitions/transition.productPreparation.js';
|
|
11
|
+
import { transitionProductActionsForRecord } from '../transitions/transition.productPresentation.js';
|
|
12
|
+
import { CANONICAL_TOPIC_TO_TASK_BUNDLED_SOURCE_ID } from '../transitions/canonicalTransition.semanticPackage.js';
|
|
13
|
+
import '../workspaces/workspace.lifecycle.js';
|
|
14
|
+
|
|
15
|
+
const lifecycle = globalThis.TiinexWorkspaceLifecycle;
|
|
16
|
+
assert.ok(lifecycle?.addWorkspaceRecord, 'workspace lifecycle must be available');
|
|
17
|
+
|
|
18
|
+
const cacheCommit = 'd69b8ff55a56b8cb9282b8684db6a938a4435b94';
|
|
19
|
+
const cacheUrlBySchema = Object.freeze({
|
|
20
|
+
'tiinex.root.v1': new URL(`../transitions/canonical-schema-cache/${cacheCommit}/tiinex.root.v1.schema.md`, import.meta.url),
|
|
21
|
+
'tiinex.transition.definition.v1': new URL(`../transitions/canonical-schema-cache/${cacheCommit}/tiinex.transition.definition.v1.schema.md`, import.meta.url),
|
|
22
|
+
'tiinex.task.v1': new URL('../schemas/core/task/tiinex.task.v1.schema.md', import.meta.url),
|
|
23
|
+
'tiinex.topic.v1': new URL('../transitions/canonical-schema-cache/52ecdea0a75893882ce282214d155f70e1309c2a/tiinex.topic.v1.schema.md', import.meta.url),
|
|
24
|
+
'tiinex.interpretation.v1': new URL('../schemas/core/interpretation/tiinex.interpretation.v1.schema.md', import.meta.url),
|
|
25
|
+
'tiinex.relation.v1': new URL('../transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.relation.v1.schema.md', import.meta.url),
|
|
26
|
+
'tiinex.schema.contract.v1': new URL('../transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.contract.v1.schema.md', import.meta.url),
|
|
27
|
+
'tiinex.schema.generation.v1': new URL('../transitions/canonical-schema-cache/053d46ce082d4ec261b82abc44ecca403d61e240/tiinex.schema.generation.v1.schema.md', import.meta.url)
|
|
28
|
+
});
|
|
29
|
+
const schemaCache = CANONICAL_TRANSITION_SCHEMA_CACHE_MANIFEST.map((item) => ({
|
|
30
|
+
...item,
|
|
31
|
+
markdown: fs.readFileSync(cacheUrlBySchema[item.schemaId], 'utf8'),
|
|
32
|
+
sourceQualification: 'source-qualified-cache'
|
|
33
|
+
}));
|
|
34
|
+
const canonicalDefinitionPath = 'src/schemas/core/task/.transitions/topic-to-task-transition-definition.trace.md';
|
|
35
|
+
const canonicalDefinitionMarkdown = fs.readFileSync(new URL('../schemas/core/task/.transitions/topic-to-task-transition-definition.trace.md', import.meta.url), 'utf8');
|
|
36
|
+
const bundledDefinitionSource = Object.freeze({ id: CANONICAL_TOPIC_TO_TASK_BUNDLED_SOURCE_ID, adapterId: 'static', kind: 'bundled-canonical' });
|
|
37
|
+
const bundledDefinitions = Object.freeze([{ path: canonicalDefinitionPath, title: 'Topic to Task', markdown: canonicalDefinitionMarkdown, sourceQualification: 'compiled-semantic-package-qualified', sourceMode: 'bundled-canonical-transition-definition', source: bundledDefinitionSource }]);
|
|
38
|
+
const fullValues = Object.freeze({
|
|
39
|
+
Summary: 'Canonical task',
|
|
40
|
+
Objective: 'Deliver the first canonical product vertical slice.',
|
|
41
|
+
'Done Criteria': 'Exactly one qualified local Task exists.',
|
|
42
|
+
Scope: 'Browser-local canonical Topic to Task creation only.',
|
|
43
|
+
Dependencies: 'One recoverable source-backed Topic.'
|
|
44
|
+
});
|
|
45
|
+
const fixedRef = '1111111111111111111111111111111111111111';
|
|
46
|
+
|
|
47
|
+
function participantMarkdown(schemaId = 'tiinex.topic.v1', title = 'Source Topic') {
|
|
48
|
+
return `# Continuity Context\n\n- Envelope Schema: tiinex.root.v1\n- Current\n - Current Schema: ${schemaId}\n - Created At: 2026-08-16 00:00:00\n - Summary: ${title}\n\n---\n\n# ${title}\n\nReadable participant.\n\n# Continuity Integrity\n\n- product-fixture-v1\n - Towards: self\n - Value: ${title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}\n`;
|
|
49
|
+
}
|
|
50
|
+
function sourceBackedRecord({ id = 'topic-1', title = 'Source Topic', schemaId = 'tiinex.topic.v1', path = '.topics/source-topic.trace.md', ref = fixedRef, materializedCommit = '', repository = 'Tiinex/docs', workspaceId = 'workspace-1' } = {}) {
|
|
51
|
+
return Object.assign(createRecordFromMarkdown(participantMarkdown(schemaId, title), { path, name: title, sourceMode: 'source-backed' }), {
|
|
52
|
+
id, workspaceId, title, kind: schemaId, schemaId, path, sourceMode: 'source-backed',
|
|
53
|
+
source: { id: `github:${repository}`, kind: 'github-tree', adapterId: 'github', repository, repo: repository, ref, rootPath: '' },
|
|
54
|
+
sourceTarget: { sourceArtifactPath: path, inputTarget: path, ...(materializedCommit ? { materializedCommit } : {}) }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function localRecord({ id = 'topic-local', title = 'Local Topic', schemaId = 'tiinex.topic.v1', workspaceId = 'workspace-1' } = {}) {
|
|
58
|
+
const path = `.topics/${id}.trace.md`;
|
|
59
|
+
return Object.assign(createRecordFromMarkdown(participantMarkdown(schemaId, title), { path, name: title, sourceMode: 'local' }), {
|
|
60
|
+
id, workspaceId, title, kind: schemaId, schemaId, path, sourceMode: 'local', source: { id: 'local', adapterId: 'local', kind: 'local-session' }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function definitionRecord(markdown = canonicalDefinitionMarkdown, id = 'workspace-definition') {
|
|
64
|
+
return Object.assign(createRecordFromMarkdown(markdown, { path: `.topics/transitions/${id}.trace.md`, name: 'Topic to Task', sourceMode: 'local' }), { id, workspaceId: 'workspace-1', source: { id: 'local', adapterId: 'local', kind: 'local-session' } });
|
|
65
|
+
}
|
|
66
|
+
function appState(records, workspaceId = 'workspace-1') {
|
|
67
|
+
return {
|
|
68
|
+
version: 1,
|
|
69
|
+
activeWorkspaceId: workspaceId,
|
|
70
|
+
view: { universe: 'column', workspaceVerse: 'feed', reader: 'scan', query: '' },
|
|
71
|
+
workspaces: [{ id: workspaceId, name: 'Product', title: 'Product', createdAt: '2026-08-17T01:00:00.000Z', kind: 'workspace', source: { id: 'local', adapterId: 'local', kind: 'local-session' }, sources: [], sourceOrder: [], records, assets: [], importLog: [], mode: 'feed' }],
|
|
72
|
+
audit: null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function prep(currentRecord, records, overrides = {}) {
|
|
76
|
+
return prepareCanonicalTransitionProductActions({ currentRecord, workspaceRecords: records, workspaceId: overrides.workspaceId === undefined ? 'workspace-1' : overrides.workspaceId, schemaCache: overrides.schemaCache || schemaCache, bundledDefinitions: overrides.bundledDefinitions === undefined ? bundledDefinitions : overrides.bundledDefinitions });
|
|
77
|
+
}
|
|
78
|
+
function capableAction(preparation) { return (preparation.actions || []).find((action) => action.canonicalIdentifier === 'tiinex.site.topic-to-task.v1'); }
|
|
79
|
+
function countingLifecycle() {
|
|
80
|
+
let adds = 0;
|
|
81
|
+
return {
|
|
82
|
+
lifecycle: Object.freeze({ ...lifecycle, addWorkspaceRecord(...args) { adds += 1; return lifecycle.addWorkspaceRecord(...args); } }),
|
|
83
|
+
count: () => adds
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function execute({ state, currentRecordId, definitionKey, values = fullValues, cache = schemaCache, definitions = bundledDefinitions, workspaceId = 'workspace-1' }) {
|
|
87
|
+
const counter = countingLifecycle();
|
|
88
|
+
const result = executeCanonicalTransitionLocalCreate({
|
|
89
|
+
lifecycle: counter.lifecycle,
|
|
90
|
+
state,
|
|
91
|
+
workspaceId,
|
|
92
|
+
currentRecordId,
|
|
93
|
+
definitionKey,
|
|
94
|
+
values,
|
|
95
|
+
schemaCache: cache,
|
|
96
|
+
bundledDefinitions: definitions,
|
|
97
|
+
persistenceOwnership: createPersistenceOwnershipPolicy(PersistenceRouteOwner.semanticState),
|
|
98
|
+
now: new Date('2026-08-17T01:23:45.000Z'),
|
|
99
|
+
clock: () => '2026-08-17T01:23:45.000Z'
|
|
100
|
+
});
|
|
101
|
+
return { result, adds: counter.count() };
|
|
102
|
+
}
|
|
103
|
+
function legacyFallback(record, records, options = {}) {
|
|
104
|
+
return transitionProductActionsForRecord(record, { workspaceRecords: records, workspaceId: 'workspace-1', maxPrimary: 10, schemaCache: options.schemaCache || schemaCache, bundledDefinitions: options.bundledDefinitions === undefined ? bundledDefinitions : options.bundledDefinitions });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// A/B. Exact bundled canonical definition qualifies, and current Topic is the explicit current participant even with another suitable Topic loaded.
|
|
108
|
+
const sourceTopic = sourceBackedRecord();
|
|
109
|
+
const unrelatedTopic = sourceBackedRecord({ id: 'topic-2', title: 'Unrelated Topic', path: '.topics/unrelated-topic.trace.md' });
|
|
110
|
+
const records = [sourceTopic, unrelatedTopic];
|
|
111
|
+
const prepared = prep(sourceTopic, records);
|
|
112
|
+
const action = capableAction(prepared);
|
|
113
|
+
assert.equal(prepared.state, 'prepared');
|
|
114
|
+
assert.equal(action?.definition?.artifact?.schemaId, 'tiinex.transition.definition.v1');
|
|
115
|
+
assert.equal(action?.definition?.canonicalReadQualified, true);
|
|
116
|
+
assert.equal(action?.definition?.contractValidation?.status, 'valid');
|
|
117
|
+
assert.equal(action?.definition?.artifact?.schemaId === 'tiinex.site.transition.legacy-shorthand.v1', false);
|
|
118
|
+
assert.equal(action?.availability?.availability, 'available');
|
|
119
|
+
assert.equal(action?.availability?.context?.assignment, 'unique');
|
|
120
|
+
assert.equal(action?.currentParticipant?.identity?.id, prepared.currentParticipant?.identity?.id);
|
|
121
|
+
assert.equal(action?.productCapable, true);
|
|
122
|
+
assert.deepEqual(action?.authoring?.requiredInputs, ['Summary', 'Objective', 'Done Criteria', 'Scope', 'Dependencies']);
|
|
123
|
+
assert.deepEqual(action?.authoring?.toolingConfigurationFields, ['version', 'createTitle', 'summaryPrompt', 'summaryPlaceholder', 'whyPrompt', 'whyPlaceholder']);
|
|
124
|
+
assert.equal(action?.authoring?.requiredInputs.includes('createTitle'), false, 'tooling configuration must not become authoring input');
|
|
125
|
+
assert.equal(action?.parentRecovery?.state, 'qualified');
|
|
126
|
+
assert.match(action.parentRecovery.permalink, /\/blob\/1111111111111111111111111111111111111111\/\.topics\/source-topic\.trace\.md$/);
|
|
127
|
+
|
|
128
|
+
// Workspace-loaded canonical Transition Definitions are eligible without the bundled default being present.
|
|
129
|
+
const loadedDefinition = definitionRecord();
|
|
130
|
+
const loadedPrepared = prep(sourceTopic, [sourceTopic, loadedDefinition], { bundledDefinitions: [] });
|
|
131
|
+
assert.equal(capableAction(loadedPrepared)?.productCapable, true);
|
|
132
|
+
assert.equal(capableAction(loadedPrepared)?.definition?.canonicalReadQualified, true);
|
|
133
|
+
|
|
134
|
+
// C/H/I/L. Complete fresh invocation crosses the bounded local mutation boundary exactly once.
|
|
135
|
+
const successState = appState(records);
|
|
136
|
+
const beforeSource = JSON.stringify(sourceTopic);
|
|
137
|
+
const success = execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey });
|
|
138
|
+
assert.equal(success.result.ok, true);
|
|
139
|
+
assert.equal(success.adds, 1);
|
|
140
|
+
assert.equal(success.result.bindingPlan?.qualification, 'qualified');
|
|
141
|
+
assert.equal(success.result.v423?.qualification, 'qualified');
|
|
142
|
+
assert.equal(success.result.taskQualification?.state, 'qualified');
|
|
143
|
+
assert.equal(success.result.taskQualification?.projection?.validation?.findings?.filter((finding) => finding.severity === 'error').length, 0);
|
|
144
|
+
assert.equal(success.result.record?.kind, 'tiinex.task.v1');
|
|
145
|
+
assert.equal(success.result.record?.schemaId, 'tiinex.task.v1');
|
|
146
|
+
assert.equal(success.result.record?.sourceMode, 'local-transition-canonical');
|
|
147
|
+
assert.equal(success.result.record?.status, 'local');
|
|
148
|
+
assert.equal(success.result.record?.path, '.topics/canonical-task--task.trace.md');
|
|
149
|
+
assert.equal(success.result.record?.source?.adapterId, 'local');
|
|
150
|
+
assert.equal(success.result.record?.source?.repository, undefined);
|
|
151
|
+
assert.equal(success.result.record?.source?.ref, undefined);
|
|
152
|
+
assert.equal(success.result.remoteWrite, false);
|
|
153
|
+
assert.equal(success.result.sourceMutation, false);
|
|
154
|
+
assert.equal(success.result.concretePath, '.topics/canonical-task--task.trace.md');
|
|
155
|
+
assert.equal(success.result.relationMaterialization, false);
|
|
156
|
+
assert.equal((success.result.workspace?.records || []).filter((record) => record.schemaId === 'tiinex.task.v1' || record.kind === 'tiinex.task.v1').length, 1);
|
|
157
|
+
assert.equal(JSON.stringify(success.result.workspace.records.find((record) => record.id === sourceTopic.id)), beforeSource);
|
|
158
|
+
for (const [heading, value] of [['Objective', fullValues.Objective], ['Done Criteria', fullValues['Done Criteria']], ['Scope', fullValues.Scope], ['Dependencies', fullValues.Dependencies]]) {
|
|
159
|
+
assert.ok(success.result.record.markdown.includes(`## ${heading}\n\n${value}`));
|
|
160
|
+
}
|
|
161
|
+
assert.ok(success.result.record.markdown.includes(`# ${fullValues.Summary}`));
|
|
162
|
+
assert.ok(success.result.record.markdown.includes(`- Trace: ${action.parentRecovery.trace}`));
|
|
163
|
+
assert.ok(success.result.record.markdown.includes(`- Origin: ${action.parentRecovery.origin}`));
|
|
164
|
+
assert.ok(!success.result.record.markdown.includes('## Next Step'));
|
|
165
|
+
assert.ok(!success.result.record.markdown.includes('Draft Local Integrity'));
|
|
166
|
+
|
|
167
|
+
// D. Missing one required Task generation value makes v423 incomplete and produces zero mutations.
|
|
168
|
+
const incompleteValues = { ...fullValues }; delete incompleteValues.Dependencies;
|
|
169
|
+
const incomplete = execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, values: incompleteValues });
|
|
170
|
+
assert.equal(incomplete.result.ok, false);
|
|
171
|
+
assert.equal(incomplete.result.v423?.qualification, 'incomplete');
|
|
172
|
+
assert.equal(incomplete.adds, 0);
|
|
173
|
+
|
|
174
|
+
// E. Wrong current schema cannot expose the canonical Topic→Task product capability.
|
|
175
|
+
const wrongCurrent = sourceBackedRecord({ id: 'wrong', title: 'Task-like current', schemaId: 'tiinex.task.v1', path: '.topics/wrong.trace.md' });
|
|
176
|
+
const wrongPrepared = prep(wrongCurrent, [wrongCurrent]);
|
|
177
|
+
assert.notEqual(capableAction(wrongPrepared)?.productCapable, true);
|
|
178
|
+
const wrongCommand = execute({ state: appState([wrongCurrent]), currentRecordId: wrongCurrent.id, definitionKey: 'tiinex.site.topic-to-task.v1' });
|
|
179
|
+
assert.equal(wrongCommand.result.ok, false);
|
|
180
|
+
assert.equal(wrongCommand.adds, 0);
|
|
181
|
+
|
|
182
|
+
// F. Contradictory observations for one participant identity fail closed; no unrelated record is auto-selected.
|
|
183
|
+
const conflictObservation = sourceBackedRecord({ id: 'conflict-task-observation', title: 'Conflicting observation', schemaId: 'tiinex.task.v1', path: sourceTopic.path });
|
|
184
|
+
const ambiguousPrepared = prep(sourceTopic, [sourceTopic, conflictObservation]);
|
|
185
|
+
assert.notEqual(capableAction(ambiguousPrepared)?.productCapable, true);
|
|
186
|
+
const ambiguous = execute({ state: appState([sourceTopic, conflictObservation]), currentRecordId: sourceTopic.id, definitionKey: capableAction(ambiguousPrepared)?.definitionKey || action.definitionKey });
|
|
187
|
+
assert.equal(ambiguous.result.ok, false);
|
|
188
|
+
assert.equal(ambiguous.adds, 0);
|
|
189
|
+
|
|
190
|
+
// G. A local/session Topic with a concrete path is canonically representable without fabricated remote provenance.
|
|
191
|
+
const localTopic = localRecord();
|
|
192
|
+
const localPrepared = prep(localTopic, [localTopic]);
|
|
193
|
+
const localAction = capableAction(localPrepared);
|
|
194
|
+
assert.equal(localAction?.parentRecovery?.state, 'qualified');
|
|
195
|
+
assert.equal(localAction?.parentRecovery?.representationKind, 'local-path');
|
|
196
|
+
assert.equal(localAction?.parentRecovery?.repository, '');
|
|
197
|
+
assert.equal(localAction?.productCapable, true);
|
|
198
|
+
const localCreated = execute({ state: appState([localTopic]), currentRecordId: localTopic.id, definitionKey: localAction?.definitionKey || action.definitionKey });
|
|
199
|
+
assert.equal(localCreated.result.ok, true);
|
|
200
|
+
assert.equal(localCreated.adds, 1);
|
|
201
|
+
assert.equal(localCreated.result.record.path, '.topics/canonical-task--task.trace.md');
|
|
202
|
+
assert.equal(localCreated.result.record.origin, localTopic.path);
|
|
203
|
+
const pathlessLocal = Object.assign({}, localTopic, { id: 'topic-pathless', path: '', sourceTarget: {} });
|
|
204
|
+
const pathlessLocalAction = capableAction(prep(pathlessLocal, [pathlessLocal]));
|
|
205
|
+
assert.equal(pathlessLocalAction?.parentRecovery?.state, 'unavailable');
|
|
206
|
+
assert.equal(pathlessLocalAction?.productCapable, false);
|
|
207
|
+
|
|
208
|
+
// J. Cache identity is a hard product gate: stale or missing bytes never become canonical authority.
|
|
209
|
+
const staleCache = schemaCache.map((item) => item.schemaId === 'tiinex.task.v1' ? { ...item, markdown: `${item.markdown}\n` } : item);
|
|
210
|
+
assert.equal(qualifyCanonicalTransitionSchemaCache(staleCache).sourceQualified, false);
|
|
211
|
+
const stalePrepared = prep(sourceTopic, [sourceTopic], { schemaCache: staleCache });
|
|
212
|
+
assert.equal(stalePrepared.state, 'prepared', 'output-schema cache failure must remain capability-local after v431');
|
|
213
|
+
assert.equal(capableAction(stalePrepared)?.productCapable, false);
|
|
214
|
+
assert.ok(capableAction(stalePrepared)?.capability?.reasons?.includes('creation-authority-unavailable'));
|
|
215
|
+
const missingCache = schemaCache.filter((item) => item.schemaId !== 'tiinex.task.v1');
|
|
216
|
+
const missingPrepared = prep(sourceTopic, [sourceTopic], { schemaCache: missingCache });
|
|
217
|
+
assert.equal(missingPrepared.state, 'prepared');
|
|
218
|
+
assert.equal(capableAction(missingPrepared)?.productCapable, false);
|
|
219
|
+
|
|
220
|
+
// K/M. Malformed canonical authority stays malformed; legacy remains only compatibility presentation truth.
|
|
221
|
+
const malformedDefinition = canonicalDefinitionMarkdown.replace('Effect: create-new', 'Effect: cretae-new');
|
|
222
|
+
const malformedDefinitions = [{ ...bundledDefinitions[0], markdown: malformedDefinition }];
|
|
223
|
+
const malformedPrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: malformedDefinitions });
|
|
224
|
+
const malformedAction = capableAction(malformedPrepared);
|
|
225
|
+
assert.notEqual(malformedAction?.definition?.canonicalReadQualified, true);
|
|
226
|
+
assert.notEqual(malformedAction?.productCapable, true);
|
|
227
|
+
const malformedPresented = legacyFallback(sourceTopic, [sourceTopic], { bundledDefinitions: malformedDefinitions });
|
|
228
|
+
assert.equal(malformedPresented.some((item) => item.kind === 'canonical-transition-product'), false);
|
|
229
|
+
assert.equal(malformedPresented.some((item) => item.definitionId === 'topic.continue.task'), false, 'active bundled canonical authority must not be masked by legacy fallback even when product capability is unavailable');
|
|
230
|
+
const noDefinitionPresented = legacyFallback(sourceTopic, [sourceTopic], { bundledDefinitions: [] });
|
|
231
|
+
assert.equal(noDefinitionPresented.some((item) => item.definitionId === 'topic.continue.task'), true);
|
|
232
|
+
const canonicalPresented = legacyFallback(sourceTopic, [sourceTopic]);
|
|
233
|
+
assert.equal(canonicalPresented.filter((item) => item.kind === 'canonical-transition-product').length, 1);
|
|
234
|
+
assert.equal(canonicalPresented.some((item) => item.definitionId === 'topic.continue.task'), false, 'canonical product capability suppresses the exact legacy compatibility action only');
|
|
235
|
+
|
|
236
|
+
// Missing destination/workspace and malformed Parent source metadata both produce zero mutations.
|
|
237
|
+
const missingWorkspace = execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, workspaceId: 'missing-workspace' });
|
|
238
|
+
assert.equal(missingWorkspace.result.ok, false);
|
|
239
|
+
assert.equal(missingWorkspace.adds, 0);
|
|
240
|
+
const nonPinnedTopic = sourceBackedRecord({ id: 'unpinned', title: 'Unpinned Topic', path: '.topics/unpinned.trace.md', ref: 'main' });
|
|
241
|
+
const unpinnedPrepared = prep(nonPinnedTopic, [nonPinnedTopic]);
|
|
242
|
+
assert.equal(capableAction(unpinnedPrepared)?.productCapable, false);
|
|
243
|
+
assert.equal(legacyFallback(nonPinnedTopic, [nonPinnedTopic]).some((item) => item.definitionId === 'topic.continue.task'), false, 'legacy compatibility must not mask missing immutable canonical Parent provenance');
|
|
244
|
+
const materializedBranchTopic = sourceBackedRecord({ id: 'materialized-main', title: 'Materialized main Topic', path: '.topics/materialized-main.trace.md', ref: 'main', materializedCommit: fixedRef });
|
|
245
|
+
const materializedBranchAction = capableAction(prep(materializedBranchTopic, [materializedBranchTopic]));
|
|
246
|
+
assert.equal(materializedBranchAction?.productCapable, true, 'branch source becomes canonical-capable only through immutable materialized commit authority');
|
|
247
|
+
assert.equal(materializedBranchAction?.parentRecovery?.ref, fixedRef);
|
|
248
|
+
assert.match(materializedBranchAction?.parentRecovery?.permalink || '', new RegExp(`/blob/${fixedRef}/\.topics/materialized-main\.trace\.md$`));
|
|
249
|
+
|
|
250
|
+
// Static architecture/product guards: canonical product slice never invokes legacy materialization/path semantics.
|
|
251
|
+
const commandSource = fs.readFileSync(new URL('../app/canonicalTransitionLocalCreateCommand.js', import.meta.url), 'utf8');
|
|
252
|
+
const preparationSource = fs.readFileSync(new URL('../transitions/transition.productPreparation.js', import.meta.url), 'utf8');
|
|
253
|
+
const presentationSource = fs.readFileSync(new URL('../transitions/transition.productPresentation.js', import.meta.url), 'utf8');
|
|
254
|
+
const materializerSource = fs.readFileSync(new URL('../transitions/transition.taskMaterializer.js', import.meta.url), 'utf8');
|
|
255
|
+
const dialogSource = fs.readFileSync(new URL('../schemas/workspace/workspace.canonicalTaskDialog.views.jsx', import.meta.url), 'utf8');
|
|
256
|
+
for (const forbidden of ['ensureUniqueTransitionPath', 'createContinuationDraft']) assert.equal(commandSource.includes(forbidden), false, `canonical command must not call ${forbidden}`);
|
|
257
|
+
assert.ok(commandSource.includes('allocateContinuationPath'), 'canonical command must reuse the established continuation path allocator');
|
|
258
|
+
assert.ok(commandSource.includes('buildCanonicalTransitionInvocationBindingPlan'));
|
|
259
|
+
assert.ok(commandSource.includes('buildCanonicalTransitionOutputMaterializationPlan'));
|
|
260
|
+
assert.ok(commandSource.includes('prepareCanonicalTransitionProductActions'));
|
|
261
|
+
assert.equal(preparationSource.includes("'tiinex.site.topic-to-task.v1'"), false, 'semantic product capability must not be selected by canonical identifier');
|
|
262
|
+
assert.ok(presentationSource.includes("'tiinex.site.topic-to-task.v1': 'topic.continue.task'"), 'legacy suppression bridge must remain explicit presentation policy');
|
|
263
|
+
assert.equal(materializerSource.includes('Next Step'), false);
|
|
264
|
+
assert.equal(materializerSource.includes('Draft Local Integrity'), false);
|
|
265
|
+
assert.ok(dialogSource.includes('CanonicalAuthoringDialog'), 'canonical product dialog must project the authoring contract rather than hardcode one output schema');
|
|
266
|
+
assert.ok(dialogSource.includes('action?.authoring?.requiredInputs'), 'canonical product dialog must consume schema-qualified required authoring inputs');
|
|
267
|
+
assert.equal(dialogSource.includes('const FIELDS'), false, 'canonical product dialog must not own a hardcoded Task field list');
|
|
268
|
+
assert.ok(canonicalDefinitionMarkdown.includes('Canonical Identifier: tiinex.site.topic-to-task.v1'));
|
|
269
|
+
assert.equal(canonicalDefinitionMarkdown.includes('tiinex.site.transition.legacy-shorthand.v1'), false);
|
|
270
|
+
|
|
271
|
+
// Adjacent-state/product sweep: each fail-closed axis yields zero mutations; successful mutation always used fresh v423 truth.
|
|
272
|
+
const sweep = [
|
|
273
|
+
['source-backed/exact/complete/workspace/parent/cache/canonical', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey }), true],
|
|
274
|
+
['local-topic', () => execute({ state: appState([localTopic]), currentRecordId: localTopic.id, definitionKey: localAction?.definitionKey || action.definitionKey }), true],
|
|
275
|
+
['wrong-schema', () => execute({ state: appState([wrongCurrent]), currentRecordId: wrongCurrent.id, definitionKey: action.definitionKey }), false],
|
|
276
|
+
['missing-generation-input', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, values: incompleteValues }), false],
|
|
277
|
+
['missing-destination', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, workspaceId: '' }), false],
|
|
278
|
+
['unrecoverable-parent', () => execute({ state: appState([nonPinnedTopic]), currentRecordId: nonPinnedTopic.id, definitionKey: action.definitionKey }), false],
|
|
279
|
+
['wrong-cache-bytes', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, cache: staleCache }), false],
|
|
280
|
+
['missing-cache', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, cache: missingCache }), false],
|
|
281
|
+
['malformed-definition', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, definitions: malformedDefinitions }), false],
|
|
282
|
+
['definition-absent', () => execute({ state: successState, currentRecordId: sourceTopic.id, definitionKey: action.definitionKey, definitions: [] }), false]
|
|
283
|
+
];
|
|
284
|
+
for (const [name, run, shouldSucceed] of sweep) {
|
|
285
|
+
const outcome = run();
|
|
286
|
+
assert.equal(outcome.result.ok, shouldSucceed, `${name} outcome`);
|
|
287
|
+
assert.equal(outcome.adds, shouldSucceed ? 1 : 0, `${name} mutation count`);
|
|
288
|
+
if (shouldSucceed) {
|
|
289
|
+
assert.equal(outcome.result.v423?.qualification, 'qualified');
|
|
290
|
+
assert.ok(outcome.result.record?.path, `${name} concrete local path`);
|
|
291
|
+
assert.equal(outcome.result.concretePath, outcome.result.record?.path, `${name} receipt path`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
// Architect v424 product-capability + source-authority closure batch.
|
|
298
|
+
function bundled(markdown, path = canonicalDefinitionPath, title = 'Topic to Task') {
|
|
299
|
+
return [{ path, title, markdown, sourceQualification: 'compiled-semantic-package-qualified', sourceMode: 'bundled-canonical-transition-definition', source: bundledDefinitionSource }];
|
|
300
|
+
}
|
|
301
|
+
function actionById(preparation, id) { return (preparation.actions || []).find((candidate) => candidate.canonicalIdentifier === id); }
|
|
302
|
+
function mutatedDefinition(id, edits = []) {
|
|
303
|
+
let markdown = canonicalDefinitionMarkdown.replace('Canonical Identifier: tiinex.site.topic-to-task.v1', `Canonical Identifier: ${id}`);
|
|
304
|
+
for (const [from, to] of edits) markdown = markdown.replace(from, to);
|
|
305
|
+
return markdown;
|
|
306
|
+
}
|
|
307
|
+
function assertNoMutation(outcome, label) {
|
|
308
|
+
assert.equal(outcome.result.ok, false, `${label}: outcome`);
|
|
309
|
+
assert.equal(outcome.adds, 0, `${label}: mutation count`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// A/E. Product authority is anchored to the exact current-assigned Topic Parent role, not product identity.
|
|
313
|
+
const signalId = 'tiinex.site.signal-to-task.v1';
|
|
314
|
+
const signalDefinitionMarkdown = mutatedDefinition(signalId, [['Schema Constraint: tiinex.topic.v1', 'Schema Constraint: tiinex.signal.v1']]);
|
|
315
|
+
const signalRecord = sourceBackedRecord({ id: 'signal-1', title: 'Source Signal', schemaId: 'tiinex.signal.v1', path: '.signals/source-signal.trace.md' });
|
|
316
|
+
const signalPrepared = prep(signalRecord, [signalRecord], { bundledDefinitions: bundled(signalDefinitionMarkdown) });
|
|
317
|
+
const signalAction = actionById(signalPrepared, signalId);
|
|
318
|
+
assert.equal(signalAction?.definition?.canonicalReadQualified, true);
|
|
319
|
+
assert.equal(signalAction?.availability?.availability, 'available');
|
|
320
|
+
assert.equal(signalAction?.parentRecovery?.state, 'qualified');
|
|
321
|
+
assert.equal(signalAction?.parentRecovery?.schemaId, 'tiinex.signal.v1');
|
|
322
|
+
assert.equal(signalAction?.productCapable, true, 'v431 generic canonical local-create pattern supports an exact one-input Signal→Task definition without Topic hardcoding');
|
|
323
|
+
assert.deepEqual(signalAction?.capability?.reasons || [], []);
|
|
324
|
+
const signalExecution = execute({ state: appState([signalRecord]), currentRecordId: signalRecord.id, definitionKey: signalAction.definitionKey, definitions: bundled(signalDefinitionMarkdown) });
|
|
325
|
+
assert.equal(signalExecution.result.ok, true, 'Signal→Task now executes through the same authority-driven canonical pattern');
|
|
326
|
+
assert.equal(signalExecution.adds, 1);
|
|
327
|
+
assert.equal(signalExecution.result.record?.schemaId, 'tiinex.task.v1');
|
|
328
|
+
assert.equal(signalExecution.result.record?.parentSchemaId, 'tiinex.signal.v1');
|
|
329
|
+
|
|
330
|
+
const parentOtherRoleId = 'tiinex.site.topic-task-parent-role.v1';
|
|
331
|
+
const parentOtherRoleDefinition = mutatedDefinition(parentOtherRoleId, [
|
|
332
|
+
['\n## Output Roles', '\n- parent-task\n - Meaning: Different required Parent role.\n - Minimum Count: 1\n - Maximum Count: 1\n - Target Kind: artifact\n - Schema Constraint: tiinex.task.v1\n - Acquisition Policy: existing-only\n\n## Output Roles'],
|
|
333
|
+
['Parent Binding: source-topic', 'Parent Binding: parent-task']
|
|
334
|
+
]);
|
|
335
|
+
const existingTaskParent = sourceBackedRecord({ id: 'existing-task-parent', title: 'Existing Task Parent', schemaId: 'tiinex.task.v1', path: '.tasks/existing-task.trace.md' });
|
|
336
|
+
const parentOtherPrepared = prep(sourceTopic, [sourceTopic, existingTaskParent], { bundledDefinitions: bundled(parentOtherRoleDefinition) });
|
|
337
|
+
const parentOtherAction = actionById(parentOtherPrepared, parentOtherRoleId);
|
|
338
|
+
assert.equal(parentOtherAction?.definition?.canonicalReadQualified, true);
|
|
339
|
+
assert.equal(parentOtherAction?.availability?.context?.assignment, 'unique');
|
|
340
|
+
assert.equal(parentOtherAction?.productCapable, false);
|
|
341
|
+
assert.ok(parentOtherAction?.capability?.reasons?.includes('parent-role-not-current-artifact-role'));
|
|
342
|
+
assertNoMutation(execute({ state: appState([sourceTopic, existingTaskParent]), currentRecordId: sourceTopic.id, definitionKey: parentOtherAction.definitionKey, definitions: bundled(parentOtherRoleDefinition) }), 'Parent role must be current-assigned role');
|
|
343
|
+
|
|
344
|
+
const unsupportedShapeCases = [
|
|
345
|
+
['parent-target-kind', mutatedDefinition('tiinex.site.topic-parent-kind.v1', [[' - Target Kind: artifact\n - Schema Constraint: tiinex.topic.v1', ' - Target Kind: non-artifact']])],
|
|
346
|
+
['parent-cardinality', mutatedDefinition('tiinex.site.topic-parent-cardinality.v1', [[' - Minimum Count: 1\n - Maximum Count: 1\n - Target Kind: artifact\n - Schema Constraint: tiinex.topic.v1', ' - Minimum Count: 0\n - Maximum Count: 1\n - Target Kind: artifact\n - Schema Constraint: tiinex.topic.v1']])],
|
|
347
|
+
['parent-acquisition', mutatedDefinition('tiinex.site.topic-parent-acquisition.v1', [[' - Acquisition Policy: existing-only', ' - Acquisition Policy: existing-or-create']])],
|
|
348
|
+
['output-cardinality', mutatedDefinition('tiinex.site.topic-output-cardinality.v1', [['- task\n - Meaning: Task created by the invocation.\n - Minimum Count: 1\n - Maximum Count: 1', '- task\n - Meaning: Task created by the invocation.\n - Minimum Count: 0\n - Maximum Count: 1']])],
|
|
349
|
+
['lifecycle-revise', mutatedDefinition('tiinex.site.topic-lifecycle-revise.v1', [[' - Effect: create-new\n - Logical Continuity: new-subject\n - Required Materialization Operation: create', ' - Effect: revise-current\n - Logical Continuity: preserve-subject\n - Required Materialization Operation: revise']])],
|
|
350
|
+
['placement-override', mutatedDefinition('tiinex.site.topic-placement-override.v1', [[' - Explicit Override Allowed: no', ' - Explicit Override Allowed: yes']])]
|
|
351
|
+
];
|
|
352
|
+
for (const [label, markdown] of unsupportedShapeCases) {
|
|
353
|
+
const id = markdown.match(/Canonical Identifier: ([^\n]+)/)?.[1];
|
|
354
|
+
const p = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(markdown) });
|
|
355
|
+
const a = actionById(p, id);
|
|
356
|
+
assert.notEqual(a?.productCapable, true, `${label}: capability unavailable`);
|
|
357
|
+
const key = a?.definitionKey || action.definitionKey;
|
|
358
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: key, definitions: bundled(markdown) }), label);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const missingParentRoleDefinition = mutatedDefinition('tiinex.site.topic-parent-missing.v1', [['Parent Binding: source-topic', 'Parent Binding: missing-role']]);
|
|
362
|
+
const missingParentRolePrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(missingParentRoleDefinition) });
|
|
363
|
+
const missingParentRoleAction = actionById(missingParentRolePrepared, 'tiinex.site.topic-parent-missing.v1');
|
|
364
|
+
assert.notEqual(missingParentRoleAction?.productCapable, true);
|
|
365
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: missingParentRoleAction?.definitionKey || action.definitionKey, definitions: bundled(missingParentRoleDefinition) }), 'missing Parent role');
|
|
366
|
+
|
|
367
|
+
const wrongOutputSchemaDefinition = mutatedDefinition('tiinex.site.topic-output-schema.v1', [[' - Schema Constraint: tiinex.task.v1\n - Generation Binding: target-schema', ' - Schema Constraint: tiinex.topic.v1\n - Generation Binding: target-schema']]);
|
|
368
|
+
const wrongOutputSchemaPrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(wrongOutputSchemaDefinition) });
|
|
369
|
+
const wrongOutputSchemaAction = actionById(wrongOutputSchemaPrepared, 'tiinex.site.topic-output-schema.v1');
|
|
370
|
+
assert.equal(wrongOutputSchemaAction?.productCapable, true, 'v452 schema-owned materialization allows a qualified Topic output without adding a generic schema-id branch');
|
|
371
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: wrongOutputSchemaAction?.definitionKey || action.definitionKey, definitions: bundled(wrongOutputSchemaDefinition) }), 'Topic output without required Topic generation inputs must still fail closed');
|
|
372
|
+
|
|
373
|
+
const noMaterializationDefinition = mutatedDefinition('tiinex.site.topic-placement-none.v1', [[' - Placement Intent: new-materialization', ' - Placement Intent: no-materialization']]);
|
|
374
|
+
const noMaterializationPrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(noMaterializationDefinition) });
|
|
375
|
+
const noMaterializationAction = actionById(noMaterializationPrepared, 'tiinex.site.topic-placement-none.v1');
|
|
376
|
+
assert.notEqual(noMaterializationAction?.productCapable, true);
|
|
377
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: noMaterializationAction?.definitionKey || action.definitionKey, definitions: bundled(noMaterializationDefinition) }), 'unsupported placement intent');
|
|
378
|
+
|
|
379
|
+
const relationDefinition = mutatedDefinition('tiinex.site.topic-relation.v1', [[
|
|
380
|
+
'## Relation Effects\n\n- none',
|
|
381
|
+
'## Relation Effects\n\n- task-derived-from-topic\n - Effect: declare\n - Subject Binding: task\n - Predicate Identifier: derived-from\n - Predicate Meaning: Task is derived from Topic.\n - Object Binding: source-topic\n - Directionality: directed\n - Member Mapping: pairwise'
|
|
382
|
+
]]);
|
|
383
|
+
const relationPrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(relationDefinition) });
|
|
384
|
+
const relationAction = actionById(relationPrepared, 'tiinex.site.topic-relation.v1');
|
|
385
|
+
assert.equal(relationAction?.definition?.canonicalReadQualified, true);
|
|
386
|
+
assert.equal(relationAction?.productCapable, false);
|
|
387
|
+
assert.ok((relationAction?.capability?.reasons || []).length > 0, 'relation-bearing shape remains fail-closed unless it satisfies a separately qualified bounded Reference capability');
|
|
388
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: relationAction.definitionKey, definitions: bundled(relationDefinition) }), 'relation effect unsupported');
|
|
389
|
+
|
|
390
|
+
// B. Repo-shaped fields on local/session material must never manufacture GitHub provenance.
|
|
391
|
+
const repoShapedLocal = Object.assign({}, localRecord({ id: 'repo-shaped-local' }), {
|
|
392
|
+
source: { id: 'local', adapterId: 'local', kind: 'local-session', repository: 'Tiinex/docs', repo: 'Tiinex/docs', ref: fixedRef },
|
|
393
|
+
sourceTarget: { sourceArtifactPath: '.topics/fake.trace.md', inputTarget: '.topics/fake.trace.md' }
|
|
394
|
+
});
|
|
395
|
+
const repoShapedLocalPrepared = prep(repoShapedLocal, [repoShapedLocal]);
|
|
396
|
+
const repoShapedLocalAction = capableAction(repoShapedLocalPrepared);
|
|
397
|
+
assert.equal(repoShapedLocalAction?.parentRecovery?.state, 'qualified');
|
|
398
|
+
assert.equal(repoShapedLocalAction?.parentRecovery?.representationKind, 'local-path');
|
|
399
|
+
assert.equal(repoShapedLocalAction?.parentRecovery?.repository, '');
|
|
400
|
+
assert.equal(repoShapedLocalAction?.productCapable, true);
|
|
401
|
+
const repoShapedLocalCreated = execute({ state: appState([repoShapedLocal]), currentRecordId: repoShapedLocal.id, definitionKey: repoShapedLocalAction?.definitionKey || action.definitionKey });
|
|
402
|
+
assert.equal(repoShapedLocalCreated.result.ok, true);
|
|
403
|
+
assert.equal(repoShapedLocalCreated.adds, 1);
|
|
404
|
+
assert.equal(repoShapedLocalCreated.result.record.source?.adapterId, 'local');
|
|
405
|
+
assert.equal(repoShapedLocalCreated.result.record.markdown.includes('github.com/Tiinex/docs/blob/'), false);
|
|
406
|
+
|
|
407
|
+
const staticShaped = Object.assign({}, sourceBackedRecord({ id: 'static-shaped', path: '.topics/static.trace.md' }), {
|
|
408
|
+
sourceMode: 'static-bootstrap',
|
|
409
|
+
source: { id: 'static', adapterId: 'static', kind: 'static-bootstrap', repository: 'Tiinex/docs', repo: 'Tiinex/docs', ref: fixedRef }
|
|
410
|
+
});
|
|
411
|
+
const staticPrepared = prep(staticShaped, [staticShaped]);
|
|
412
|
+
assert.equal(capableAction(staticPrepared)?.parentRecovery?.state, 'unavailable');
|
|
413
|
+
assert.equal(capableAction(staticPrepared)?.productCapable, false);
|
|
414
|
+
|
|
415
|
+
const githubLocalMode = Object.assign({}, sourceBackedRecord({ id: 'github-local-mode', path: '.topics/github-local.trace.md' }), { sourceMode: 'local-session' });
|
|
416
|
+
const githubLocalPrepared = prep(githubLocalMode, [githubLocalMode]);
|
|
417
|
+
assert.equal(capableAction(githubLocalPrepared)?.parentRecovery?.state, 'unavailable');
|
|
418
|
+
assert.equal(capableAction(githubLocalPrepared)?.productCapable, false);
|
|
419
|
+
|
|
420
|
+
// C/E. Canonical Identifier is local semantic truth, never global definition identity.
|
|
421
|
+
const duplicateDefinition = definitionRecord(canonicalDefinitionMarkdown, 'workspace-duplicate-definition');
|
|
422
|
+
const duplicatePrepared = prep(sourceTopic, [sourceTopic, duplicateDefinition]);
|
|
423
|
+
const duplicateActions = duplicatePrepared.actions.filter((candidate) => candidate.canonicalIdentifier === 'tiinex.site.topic-to-task.v1');
|
|
424
|
+
assert.equal(duplicateActions.length, 2);
|
|
425
|
+
assert.equal(new Set(duplicateActions.map((candidate) => candidate.definitionKey)).size, 2, 'execution keys must remain registry-specific');
|
|
426
|
+
assert.equal(duplicateActions.every((candidate) => candidate.productCapable === true), true, 'same local Canonical Identifier must not globally conflict');
|
|
427
|
+
assert.equal(duplicateActions.every((candidate) => candidate.identityConflict === null), true);
|
|
428
|
+
assert.equal(duplicatePrepared.identityConflicts.length, 0);
|
|
429
|
+
for (const candidate of duplicateActions) {
|
|
430
|
+
const outcome = execute({ state: appState([sourceTopic, duplicateDefinition]), currentRecordId: sourceTopic.id, definitionKey: candidate.definitionKey });
|
|
431
|
+
assert.equal(outcome.result.ok, true, 'each registry-specific same-id key remains executable');
|
|
432
|
+
assert.equal(outcome.adds, 1);
|
|
433
|
+
}
|
|
434
|
+
assertNoMutation(execute({ state: appState([sourceTopic, duplicateDefinition]), currentRecordId: sourceTopic.id, definitionKey: 'tiinex.site.topic-to-task.v1' }), 'legacy canonical-id lookup must not select first definition');
|
|
435
|
+
|
|
436
|
+
const divergentSameIdMarkdown = canonicalDefinitionMarkdown.replace('Human Label: Create task', 'Human Label: Create a divergent task');
|
|
437
|
+
const divergentSameId = definitionRecord(divergentSameIdMarkdown, 'workspace-divergent-same-id');
|
|
438
|
+
const divergentPrepared = prep(sourceTopic, [sourceTopic, divergentSameId]);
|
|
439
|
+
const divergentActions = divergentPrepared.actions.filter((candidate) => candidate.canonicalIdentifier === 'tiinex.site.topic-to-task.v1');
|
|
440
|
+
assert.equal(divergentActions.length, 2);
|
|
441
|
+
assert.equal(divergentActions.every((candidate) => candidate.definition?.canonicalReadQualified === true), true);
|
|
442
|
+
assert.equal(divergentActions.every((candidate) => candidate.productCapable === true), true);
|
|
443
|
+
assert.equal(divergentPrepared.identityConflicts.length, 0);
|
|
444
|
+
const divergentWorkspaceAction = divergentActions.find((candidate) => candidate.definition.source?.sourceMode !== 'bundled-canonical-transition-definition');
|
|
445
|
+
const divergentExecution = execute({ state: appState([sourceTopic, divergentSameId]), currentRecordId: sourceTopic.id, definitionKey: divergentWorkspaceAction.definitionKey });
|
|
446
|
+
assert.equal(divergentExecution.result.ok, true);
|
|
447
|
+
assert.equal(divergentExecution.adds, 1);
|
|
448
|
+
|
|
449
|
+
const uniqueWorkspaceId = 'tiinex.site.topic-to-task.workspace.v1';
|
|
450
|
+
const uniqueWorkspaceMarkdown = mutatedDefinition(uniqueWorkspaceId, [['Human Label: Create task', 'Human Label: Create workspace-qualified task']]);
|
|
451
|
+
const uniqueWorkspaceDefinition = definitionRecord(uniqueWorkspaceMarkdown, 'workspace-unique-definition');
|
|
452
|
+
const uniquePrepared = prep(sourceTopic, [sourceTopic, uniqueWorkspaceDefinition]);
|
|
453
|
+
const uniqueCanonicalActions = uniquePrepared.actions.filter((candidate) => candidate.productCapable);
|
|
454
|
+
assert.equal(uniqueCanonicalActions.length, 2, 'unique canonical identities with supported semantic shape remain eligible');
|
|
455
|
+
assert.equal(new Set(uniqueCanonicalActions.map((candidate) => candidate.definitionKey)).size, 2);
|
|
456
|
+
const uniqueWorkspaceAction = actionById(uniquePrepared, uniqueWorkspaceId);
|
|
457
|
+
const uniqueExecution = execute({ state: appState([sourceTopic, uniqueWorkspaceDefinition]), currentRecordId: sourceTopic.id, definitionKey: uniqueWorkspaceAction.definitionKey });
|
|
458
|
+
assert.equal(uniqueExecution.result.ok, true);
|
|
459
|
+
assert.equal(uniqueExecution.adds, 1);
|
|
460
|
+
|
|
461
|
+
const malformedWorkspaceDefinition = definitionRecord(canonicalDefinitionMarkdown.replace('Effect: create-new', 'Effect: cretae-new'), 'workspace-malformed-definition');
|
|
462
|
+
const malformedPlusValid = prep(sourceTopic, [sourceTopic, malformedWorkspaceDefinition]);
|
|
463
|
+
assert.equal(capableAction(malformedPlusValid)?.productCapable, true, 'malformed same-id observation is not a qualified identity competitor');
|
|
464
|
+
assert.equal(malformedPlusValid.identityConflicts.length, 0);
|
|
465
|
+
|
|
466
|
+
// D. Parent reference construction is Markdown-Link-safe before capability exposure.
|
|
467
|
+
for (const [label, sourcePath, expectedFragment] of [
|
|
468
|
+
['space', '.topics/space topic.trace.md', 'space%20topic.trace.md'],
|
|
469
|
+
['close-paren', '.topics/foo)bar.trace.md', 'foo%29bar.trace.md'],
|
|
470
|
+
['non-ascii', '.topics/räksmörgås.trace.md', 'r%C3%A4ksm%C3%B6rg%C3%A5s.trace.md']
|
|
471
|
+
]) {
|
|
472
|
+
const unusual = sourceBackedRecord({ id: `path-${label}`, title: `Path ${label}`, path: sourcePath });
|
|
473
|
+
const unusualPrepared = prep(unusual, [unusual]);
|
|
474
|
+
const unusualAction = capableAction(unusualPrepared);
|
|
475
|
+
assert.equal(unusualAction?.parentRecovery?.state, 'qualified', `${label}: parent qualified`);
|
|
476
|
+
assert.equal(unusualAction?.productCapable, true, `${label}: capability exposed only with safe target`);
|
|
477
|
+
assert.ok(unusualAction.parentRecovery.permalink.includes(expectedFragment), `${label}: encoded path`);
|
|
478
|
+
assert.equal(/[)\s]/.test(unusualAction.parentRecovery.permalink), false, `${label}: Markdown target lexical safety`);
|
|
479
|
+
assert.equal(unusualAction.parentRecovery.path, sourcePath, `${label}: original repo-relative path preserved`);
|
|
480
|
+
const unusualOutcome = execute({ state: appState([unusual]), currentRecordId: unusual.id, definitionKey: unusualAction.definitionKey });
|
|
481
|
+
assert.equal(unusualOutcome.result.ok, true, `${label}: valid unusual path creates after safe encoding`);
|
|
482
|
+
assert.equal(unusualOutcome.adds, 1, `${label}: exactly one mutation`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
// Mandatory closure sweep: source/current/path/definition/role failures are fail-closed with zero lifecycle mutation.
|
|
487
|
+
const closureSweep = [
|
|
488
|
+
['topic-github-ordinary', sourceTopic, bundledDefinitions, action.definitionKey, true],
|
|
489
|
+
['signal-github', signalRecord, bundled(signalDefinitionMarkdown), signalAction.definitionKey, true],
|
|
490
|
+
['task-github', wrongCurrent, bundledDefinitions, action.definitionKey, false],
|
|
491
|
+
['topic-local-shaped', repoShapedLocal, bundledDefinitions, repoShapedLocalAction?.definitionKey || action.definitionKey, true],
|
|
492
|
+
['topic-static-shaped', staticShaped, bundledDefinitions, capableAction(staticPrepared)?.definitionKey || action.definitionKey, false],
|
|
493
|
+
['topic-github-local-mode', githubLocalMode, bundledDefinitions, capableAction(githubLocalPrepared)?.definitionKey || action.definitionKey, false],
|
|
494
|
+
['duplicate-definition', sourceTopic, bundledDefinitions, duplicateActions[0].definitionKey, true, [duplicateDefinition]],
|
|
495
|
+
['divergent-same-id', sourceTopic, bundledDefinitions, divergentActions[0].definitionKey, true, [divergentSameId]],
|
|
496
|
+
['parent-other-role', sourceTopic, bundled(parentOtherRoleDefinition), parentOtherAction.definitionKey, false, [existingTaskParent]]
|
|
497
|
+
];
|
|
498
|
+
for (const [label, current, definitions, key, shouldSucceed, extraRecords = []] of closureSweep) {
|
|
499
|
+
const outcome = execute({ state: appState([current, ...extraRecords]), currentRecordId: current.id, definitionKey: key, definitions });
|
|
500
|
+
assert.equal(outcome.result.ok, shouldSucceed, `${label}: closure outcome`);
|
|
501
|
+
assert.equal(outcome.adds, shouldSucceed ? 1 : 0, `${label}: closure mutation count`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
console.log('post-v424 product capability/source authority/definition identity/path closure sweep: PASS');
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
// Architect final v424 exact product-shape + scoped-definition-identity source closure.
|
|
508
|
+
function expectUnsupportedProduct({ label, markdown, current = sourceTopic, extraRecords = [], id = null }) {
|
|
509
|
+
const canonicalId = id || markdown.match(/Canonical Identifier: ([^\n]+)/)?.[1];
|
|
510
|
+
const preparedCase = prep(current, [current, ...extraRecords], { bundledDefinitions: bundled(markdown) });
|
|
511
|
+
const caseAction = actionById(preparedCase, canonicalId);
|
|
512
|
+
assert.ok(caseAction, `${label}: action observable`);
|
|
513
|
+
assert.notEqual(caseAction.productCapable, true, `${label}: product capability unavailable`);
|
|
514
|
+
const outcome = execute({ state: appState([current, ...extraRecords]), currentRecordId: current.id, definitionKey: caseAction.definitionKey, definitions: bundled(markdown) });
|
|
515
|
+
assertNoMutation(outcome, label);
|
|
516
|
+
return { prepared: preparedCase, action: caseAction, outcome };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// A/C. The local mechanism owns one exact lifecycle shape; explicit incompatible truth is never erased by 1x1 convenience.
|
|
520
|
+
const preserveSubjectDefinition = mutatedDefinition('tiinex.site.topic-preserve-subject.v1', [['Logical Continuity: new-subject', 'Logical Continuity: preserve-subject']]);
|
|
521
|
+
const preserveSubjectCase = expectUnsupportedProduct({ label: 'create-new + preserve-subject', markdown: preserveSubjectDefinition });
|
|
522
|
+
assert.equal(preserveSubjectCase.action.definition.canonicalReadQualified, true);
|
|
523
|
+
assert.equal(preserveSubjectCase.action.resultSemantics.qualification, 'qualified');
|
|
524
|
+
assert.ok(preserveSubjectCase.action.capability.reasons.includes('unsupported-lifecycle-capability'));
|
|
525
|
+
|
|
526
|
+
const resultBindingDefinition = mutatedDefinition('tiinex.site.topic-result-binding.v1', [[
|
|
527
|
+
' - Target Binding: task\n - Effect: create-new',
|
|
528
|
+
' - Target Binding: task\n - Result Binding: source-topic\n - Effect: create-new'
|
|
529
|
+
]]);
|
|
530
|
+
const resultBindingCase = expectUnsupportedProduct({ label: 'lifecycle Result Binding present', markdown: resultBindingDefinition });
|
|
531
|
+
assert.ok(resultBindingCase.action.capability.reasons.includes('unsupported-lifecycle-capability'));
|
|
532
|
+
|
|
533
|
+
const lifecycleCustomMappingDefinition = mutatedDefinition('tiinex.site.topic-lifecycle-custom-map.v1', [[
|
|
534
|
+
' - Required Materialization Operation: create',
|
|
535
|
+
' - Required Materialization Operation: create\n - Member Mapping: custom\n - Mapping Meaning: Caller-defined custom mapping.'
|
|
536
|
+
]]);
|
|
537
|
+
const lifecycleCustomCase = expectUnsupportedProduct({ label: 'lifecycle custom mapping', markdown: lifecycleCustomMappingDefinition });
|
|
538
|
+
assert.ok(lifecycleCustomCase.action.capability.reasons.includes('unsupported-lifecycle-capability'));
|
|
539
|
+
|
|
540
|
+
const parentCustomMappingDefinition = mutatedDefinition('tiinex.site.topic-parent-custom-map.v1', [[
|
|
541
|
+
' - Parent Binding: source-topic\n - Effect: set',
|
|
542
|
+
' - Parent Binding: source-topic\n - Effect: set\n - Member Mapping: custom\n - Mapping Meaning: Caller-defined Parent mapping.'
|
|
543
|
+
]]);
|
|
544
|
+
const parentCustomCase = expectUnsupportedProduct({ label: 'Parent custom mapping', markdown: parentCustomMappingDefinition });
|
|
545
|
+
assert.ok(parentCustomCase.action.capability.reasons.includes('unsupported-parent-capability'));
|
|
546
|
+
|
|
547
|
+
const preserveWhyDefinition = mutatedDefinition('tiinex.site.topic-preserve-why.v1', [[
|
|
548
|
+
' - Required Materialization Operation: create',
|
|
549
|
+
' - Required Materialization Operation: create\n - Preserve Why: yes'
|
|
550
|
+
]]);
|
|
551
|
+
const preserveWhyCase = expectUnsupportedProduct({ label: 'Preserve Why yes without preservation mechanism', markdown: preserveWhyDefinition });
|
|
552
|
+
assert.ok(preserveWhyCase.action.capability.reasons.includes('unsupported-lifecycle-capability'));
|
|
553
|
+
|
|
554
|
+
// Explicit single mapping is proven equivalent for this exact 1x1 local mechanism.
|
|
555
|
+
const lifecycleSingleMapping = mutatedDefinition('tiinex.site.topic-lifecycle-single-map.v1', [[
|
|
556
|
+
' - Required Materialization Operation: create',
|
|
557
|
+
' - Required Materialization Operation: create\n - Member Mapping: single'
|
|
558
|
+
]]);
|
|
559
|
+
const lifecycleSinglePrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(lifecycleSingleMapping) });
|
|
560
|
+
const lifecycleSingleAction = actionById(lifecycleSinglePrepared, 'tiinex.site.topic-lifecycle-single-map.v1');
|
|
561
|
+
assert.equal(lifecycleSingleAction?.productCapable, true);
|
|
562
|
+
const lifecycleSingleExecution = execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: lifecycleSingleAction.definitionKey, definitions: bundled(lifecycleSingleMapping) });
|
|
563
|
+
assert.equal(lifecycleSingleExecution.result.ok, true);
|
|
564
|
+
assert.equal(lifecycleSingleExecution.adds, 1);
|
|
565
|
+
|
|
566
|
+
const parentSingleMapping = mutatedDefinition('tiinex.site.topic-parent-single-map.v1', [[
|
|
567
|
+
' - Parent Binding: source-topic\n - Effect: set',
|
|
568
|
+
' - Parent Binding: source-topic\n - Effect: set\n - Member Mapping: single'
|
|
569
|
+
]]);
|
|
570
|
+
const parentSinglePrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(parentSingleMapping) });
|
|
571
|
+
const parentSingleAction = actionById(parentSinglePrepared, 'tiinex.site.topic-parent-single-map.v1');
|
|
572
|
+
assert.equal(parentSingleAction?.productCapable, true);
|
|
573
|
+
const parentSingleExecution = execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: parentSingleAction.definitionKey, definitions: bundled(parentSingleMapping) });
|
|
574
|
+
assert.equal(parentSingleExecution.result.ok, true);
|
|
575
|
+
assert.equal(parentSingleExecution.adds, 1);
|
|
576
|
+
|
|
577
|
+
// B. v424 packet/materializer owns exactly one Topic input and one Task output.
|
|
578
|
+
const extraInputDefinition = mutatedDefinition('tiinex.site.topic-extra-input.v1', [[
|
|
579
|
+
'\n## Output Roles',
|
|
580
|
+
'\n- supporting-task\n - Meaning: Additional required Task participant.\n - Minimum Count: 1\n - Maximum Count: 1\n - Target Kind: artifact\n - Schema Constraint: tiinex.task.v1\n - Acquisition Policy: existing-only\n\n## Output Roles'
|
|
581
|
+
]]);
|
|
582
|
+
const supportingTask = sourceBackedRecord({ id: 'supporting-task', title: 'Supporting Task', schemaId: 'tiinex.task.v1', path: '.tasks/supporting-task.trace.md' });
|
|
583
|
+
const extraInputPrepared = prep(sourceTopic, [sourceTopic, supportingTask], { bundledDefinitions: bundled(extraInputDefinition) });
|
|
584
|
+
const extraInputAction = actionById(extraInputPrepared, 'tiinex.site.topic-extra-input.v1');
|
|
585
|
+
assert.equal(extraInputAction?.availability?.availability, 'available');
|
|
586
|
+
assert.equal(extraInputAction?.productCapable, false);
|
|
587
|
+
assert.ok(extraInputAction?.capability?.reasons?.includes('unsupported-input-role-arity'));
|
|
588
|
+
assertNoMutation(execute({ state: appState([sourceTopic, supportingTask]), currentRecordId: sourceTopic.id, definitionKey: extraInputAction.definitionKey, definitions: bundled(extraInputDefinition) }), 'additional required input role');
|
|
589
|
+
|
|
590
|
+
const extraOutputDefinition = mutatedDefinition('tiinex.site.topic-extra-output.v1', [
|
|
591
|
+
['\n## Lifecycle And Continuity Effects', '\n- second-task\n - Meaning: Second Task output.\n - Minimum Count: 1\n - Maximum Count: 1\n - Target Kind: artifact\n - Schema Constraint: tiinex.task.v1\n - Generation Binding: target-schema\n\n## Lifecycle And Continuity Effects'],
|
|
592
|
+
[' - Target Binding: task\n - Effect: create-new', ' - Target Binding: task\n - Result Binding: second-task\n - Effect: create-new']
|
|
593
|
+
]);
|
|
594
|
+
const extraOutputPrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(extraOutputDefinition) });
|
|
595
|
+
const extraOutputAction = actionById(extraOutputPrepared, 'tiinex.site.topic-extra-output.v1');
|
|
596
|
+
assert.equal(extraOutputAction?.definition?.canonicalReadQualified, true);
|
|
597
|
+
assert.equal(extraOutputAction?.productCapable, false);
|
|
598
|
+
assert.ok(extraOutputAction?.capability?.reasons?.includes('unsupported-output-role-arity'));
|
|
599
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: extraOutputAction.definitionKey, definitions: bundled(extraOutputDefinition) }), 'additional Output Role');
|
|
600
|
+
|
|
601
|
+
// D. Repo-relative Parent path bytes are preserved; Markdown target encoding is a separate projection.
|
|
602
|
+
for (const [label, sourcePath, expectedFragment] of [
|
|
603
|
+
['internal-space-final', '.topics/internal space.trace.md', 'internal%20space.trace.md'],
|
|
604
|
+
['leading-space', ' .topics/leading.trace.md', '%20.topics/leading.trace.md'],
|
|
605
|
+
['trailing-space', '.topics/trailing.trace.md ', 'trailing.trace.md%20'],
|
|
606
|
+
['close-paren-final', '.topics/final)path.trace.md', 'final%29path.trace.md'],
|
|
607
|
+
['non-ascii-final', '.topics/åäö.trace.md', '%C3%A5%C3%A4%C3%B6.trace.md']
|
|
608
|
+
]) {
|
|
609
|
+
const pathRecord = sourceBackedRecord({ id: `final-${label}`, title: `Final ${label}`, path: sourcePath });
|
|
610
|
+
const pathPrepared = prep(pathRecord, [pathRecord]);
|
|
611
|
+
const pathAction = capableAction(pathPrepared);
|
|
612
|
+
assert.equal(pathAction?.parentRecovery?.state, 'qualified', `${label}: Parent recovery qualified`);
|
|
613
|
+
assert.equal(pathAction?.parentRecovery?.path, sourcePath, `${label}: exact repo-relative path bytes preserved`);
|
|
614
|
+
assert.ok(pathAction?.parentRecovery?.permalink.includes(expectedFragment), `${label}: encoded target`);
|
|
615
|
+
assert.equal(/[)\s]/.test(pathAction.parentRecovery.permalink), false, `${label}: Markdown target lexical safety`);
|
|
616
|
+
assert.equal(pathAction?.productCapable, true, `${label}: product-capable after safe projection`);
|
|
617
|
+
const pathOutcome = execute({ state: appState([pathRecord]), currentRecordId: pathRecord.id, definitionKey: pathAction.definitionKey });
|
|
618
|
+
assert.equal(pathOutcome.result.ok, true, `${label}: command succeeds`);
|
|
619
|
+
assert.equal(pathOutcome.adds, 1, `${label}: exactly one mutation`);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// E/F. Canonical Identifier remains local; legacy bridge belongs only to the exact Site-bundled product definition.
|
|
623
|
+
const workspaceSameIdOnlyPresented = legacyFallback(sourceTopic, [sourceTopic, duplicateDefinition], { bundledDefinitions: [] });
|
|
624
|
+
assert.equal(workspaceSameIdOnlyPresented.filter((item) => item.kind === 'canonical-transition-product').length, 1);
|
|
625
|
+
assert.equal(workspaceSameIdOnlyPresented.some((item) => item.definitionId === 'topic.continue.task'), true, 'workspace same-ID definition alone must not suppress legacy compatibility');
|
|
626
|
+
|
|
627
|
+
const malformedBundledPlusWorkspacePresented = legacyFallback(sourceTopic, [sourceTopic, duplicateDefinition], { bundledDefinitions: malformedDefinitions });
|
|
628
|
+
assert.equal(malformedBundledPlusWorkspacePresented.filter((item) => item.kind === 'canonical-transition-product').length, 1, 'workspace canonical product remains independently eligible');
|
|
629
|
+
assert.equal(malformedBundledPlusWorkspacePresented.some((item) => item.definitionId === 'topic.continue.task'), false, 'active bundled definition provenance suppresses legacy fallback even when another workspace definition remains independently capable');
|
|
630
|
+
|
|
631
|
+
const bundledPlusWorkspacePresented = legacyFallback(sourceTopic, [sourceTopic, duplicateDefinition]);
|
|
632
|
+
assert.equal(bundledPlusWorkspacePresented.filter((item) => item.kind === 'canonical-transition-product').length, 2);
|
|
633
|
+
assert.equal(bundledPlusWorkspacePresented.some((item) => item.definitionId === 'topic.continue.task'), false, 'exact product-capable bundled definition owns the migration suppression');
|
|
634
|
+
|
|
635
|
+
const workspaceUniqueOnlyPresented = legacyFallback(sourceTopic, [sourceTopic, uniqueWorkspaceDefinition], { bundledDefinitions: [] });
|
|
636
|
+
assert.equal(workspaceUniqueOnlyPresented.some((item) => item.kind === 'canonical-transition-product'), true);
|
|
637
|
+
assert.equal(workspaceUniqueOnlyPresented.some((item) => item.definitionId === 'topic.continue.task'), true, 'unrelated canonical product does not suppress legacy bridge');
|
|
638
|
+
|
|
639
|
+
assert.equal(preparationSource.includes('canonicalIdentifierConflicts'), false, 'Site must not promote local Canonical Identifier into global definition identity');
|
|
640
|
+
assert.ok(presentationSource.includes('bundled-canonical-transition-definition'), 'legacy bridge must be scoped to bundled provenance');
|
|
641
|
+
assert.ok(presentationSource.includes('CANONICAL_TOPIC_TO_TASK_BUNDLED_SOURCE_ID'), 'legacy bridge must bind to exact compiled representation provenance');
|
|
642
|
+
assert.equal(presentationSource.includes('src/transitions/definitions/topic-to-task-transition-definition.trace.md'), false, 'legacy bridge must not use the removed application-local definition path as semantic authority');
|
|
643
|
+
|
|
644
|
+
// Mandatory final product-shape sweep: every unsupported axis stays pre-capability and zero-mutation.
|
|
645
|
+
const finalShapeSweep = [
|
|
646
|
+
['lifecycle-preserve-subject', preserveSubjectCase.action, preserveSubjectDefinition, [sourceTopic]],
|
|
647
|
+
['lifecycle-result-binding', resultBindingCase.action, resultBindingDefinition, [sourceTopic]],
|
|
648
|
+
['lifecycle-custom-mapping', lifecycleCustomCase.action, lifecycleCustomMappingDefinition, [sourceTopic]],
|
|
649
|
+
['parent-custom-mapping', parentCustomCase.action, parentCustomMappingDefinition, [sourceTopic]],
|
|
650
|
+
['preserve-why', preserveWhyCase.action, preserveWhyDefinition, [sourceTopic]],
|
|
651
|
+
['extra-input', extraInputAction, extraInputDefinition, [sourceTopic, supportingTask]],
|
|
652
|
+
['extra-output', extraOutputAction, extraOutputDefinition, [sourceTopic]]
|
|
653
|
+
];
|
|
654
|
+
for (const [label, candidate, markdown, sweepRecords] of finalShapeSweep) {
|
|
655
|
+
assert.notEqual(candidate?.productCapable, true, `${label}: capability unavailable`);
|
|
656
|
+
const outcome = execute({ state: appState(sweepRecords), currentRecordId: sourceTopic.id, definitionKey: candidate.definitionKey, definitions: bundled(markdown) });
|
|
657
|
+
assertNoMutation(outcome, `${label}: final shape sweep`);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
console.log('post-v424 exact product-shape/scoped-definition-identity final source closure: PASS');
|
|
661
|
+
|
|
662
|
+
// Cross-axis sweep: arity, lifecycle/mapping, path and definition identity remain monotonic together.
|
|
663
|
+
const extraInputSingleMap = extraInputDefinition
|
|
664
|
+
.replace('Canonical Identifier: tiinex.site.topic-extra-input.v1', 'Canonical Identifier: tiinex.site.topic-extra-input-single.v1')
|
|
665
|
+
.replace(' - Required Materialization Operation: create', ' - Required Materialization Operation: create\n - Member Mapping: single');
|
|
666
|
+
const extraInputSinglePrepared = prep(sourceTopic, [sourceTopic, supportingTask], { bundledDefinitions: bundled(extraInputSingleMap) });
|
|
667
|
+
const extraInputSingleAction = actionById(extraInputSinglePrepared, 'tiinex.site.topic-extra-input-single.v1');
|
|
668
|
+
assert.equal(extraInputSingleAction?.productCapable, false);
|
|
669
|
+
assert.ok(extraInputSingleAction?.capability?.reasons?.includes('unsupported-input-role-arity'));
|
|
670
|
+
assertNoMutation(execute({ state: appState([sourceTopic, supportingTask]), currentRecordId: sourceTopic.id, definitionKey: extraInputSingleAction.definitionKey, definitions: bundled(extraInputSingleMap) }), 'two inputs + single mapping');
|
|
671
|
+
|
|
672
|
+
const extraOutputParentSingle = extraOutputDefinition
|
|
673
|
+
.replace('Canonical Identifier: tiinex.site.topic-extra-output.v1', 'Canonical Identifier: tiinex.site.topic-extra-output-parent-single.v1')
|
|
674
|
+
.replace(' - Parent Binding: source-topic\n - Effect: set', ' - Parent Binding: source-topic\n - Effect: set\n - Member Mapping: single');
|
|
675
|
+
const extraOutputParentSinglePrepared = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(extraOutputParentSingle) });
|
|
676
|
+
const extraOutputParentSingleAction = actionById(extraOutputParentSinglePrepared, 'tiinex.site.topic-extra-output-parent-single.v1');
|
|
677
|
+
assert.equal(extraOutputParentSingleAction?.productCapable, false);
|
|
678
|
+
assert.ok(extraOutputParentSingleAction?.capability?.reasons?.includes('unsupported-output-role-arity'));
|
|
679
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: extraOutputParentSingleAction.definitionKey, definitions: bundled(extraOutputParentSingle) }), 'two outputs + Parent single mapping');
|
|
680
|
+
|
|
681
|
+
const sameIdCustomMarkdown = canonicalDefinitionMarkdown.replace(
|
|
682
|
+
' - Required Materialization Operation: create',
|
|
683
|
+
' - Required Materialization Operation: create\n - Member Mapping: custom\n - Mapping Meaning: Workspace-specific custom mapping.'
|
|
684
|
+
);
|
|
685
|
+
const sameIdCustomDefinition = definitionRecord(sameIdCustomMarkdown, 'workspace-same-id-custom');
|
|
686
|
+
const sameIdCustomPrepared = prep(sourceTopic, [sourceTopic, sameIdCustomDefinition]);
|
|
687
|
+
const sameIdCustomActions = sameIdCustomPrepared.actions.filter((candidate) => candidate.canonicalIdentifier === 'tiinex.site.topic-to-task.v1');
|
|
688
|
+
assert.equal(sameIdCustomActions.length, 2);
|
|
689
|
+
assert.equal(new Set(sameIdCustomActions.map((candidate) => candidate.definitionKey)).size, 2);
|
|
690
|
+
const bundledSameIdCustomAction = sameIdCustomActions.find((candidate) => candidate.definition.source?.sourceMode === 'bundled-canonical-transition-definition');
|
|
691
|
+
const workspaceSameIdCustomAction = sameIdCustomActions.find((candidate) => candidate.definition.source?.sourceMode !== 'bundled-canonical-transition-definition');
|
|
692
|
+
assert.equal(bundledSameIdCustomAction?.productCapable, true);
|
|
693
|
+
assert.equal(workspaceSameIdCustomAction?.productCapable, false);
|
|
694
|
+
assertNoMutation(execute({ state: appState([sourceTopic, sameIdCustomDefinition]), currentRecordId: sourceTopic.id, definitionKey: workspaceSameIdCustomAction.definitionKey }), 'same-ID workspace custom mapping remains unsupported independently');
|
|
695
|
+
|
|
696
|
+
const trailingIdentityTopic = sourceBackedRecord({ id: 'trailing-identity-topic', title: 'Trailing Identity Topic', path: '.topics/identity.trace.md ' });
|
|
697
|
+
const trailingIdentityPrepared = prep(trailingIdentityTopic, [trailingIdentityTopic, duplicateDefinition]);
|
|
698
|
+
const trailingIdentityActions = trailingIdentityPrepared.actions.filter((candidate) => candidate.canonicalIdentifier === 'tiinex.site.topic-to-task.v1');
|
|
699
|
+
assert.equal(trailingIdentityActions.length, 2);
|
|
700
|
+
assert.equal(trailingIdentityActions.every((candidate) => candidate.productCapable), true);
|
|
701
|
+
assert.equal(trailingIdentityActions.every((candidate) => candidate.parentRecovery.path === '.topics/identity.trace.md '), true);
|
|
702
|
+
assert.equal(trailingIdentityActions.every((candidate) => candidate.parentRecovery.permalink.endsWith('identity.trace.md%20')), true);
|
|
703
|
+
const trailingIdentityExecution = execute({ state: appState([trailingIdentityTopic, duplicateDefinition]), currentRecordId: trailingIdentityTopic.id, definitionKey: trailingIdentityActions[1].definitionKey });
|
|
704
|
+
assert.equal(trailingIdentityExecution.result.ok, true);
|
|
705
|
+
assert.equal(trailingIdentityExecution.adds, 1);
|
|
706
|
+
|
|
707
|
+
console.log('post-v424 mandatory final product-shape/identity/path cross sweep: PASS');
|
|
708
|
+
|
|
709
|
+
// Architect v424 exact local-placement declaration closure.
|
|
710
|
+
function placementVariant(id, {
|
|
711
|
+
required = 'yes', destinationKind = '', capabilityRequirement = '', placementIntent = 'new-materialization',
|
|
712
|
+
namingAuthority = 'explicit-binding', namingReference = '', relativeTo = '', relativeMeaning = '', override = 'no'
|
|
713
|
+
} = {}) {
|
|
714
|
+
let markdown = mutatedDefinition(id);
|
|
715
|
+
markdown = markdown.replace(' - Required: yes', ` - Required: ${required}`);
|
|
716
|
+
const destinationExtras = [destinationKind && ` - Destination Kind: ${destinationKind}`, capabilityRequirement && ` - Capability Requirement: ${capabilityRequirement}`].filter(Boolean).join('\n');
|
|
717
|
+
if (destinationExtras) markdown = markdown.replace(` - Required: ${required}`, ` - Required: ${required}\n${destinationExtras}`);
|
|
718
|
+
markdown = markdown.replace(' - Placement Intent: new-materialization', ` - Placement Intent: ${placementIntent}`);
|
|
719
|
+
markdown = markdown.replace(' - Naming Authority: explicit-binding', ` - Naming Authority: ${namingAuthority}`);
|
|
720
|
+
if (namingReference) markdown = markdown.replace(` - Naming Authority: ${namingAuthority}`, ` - Naming Authority: ${namingAuthority}\n - Naming Authority Reference: ${namingReference}`);
|
|
721
|
+
if (relativeTo) markdown = markdown.replace(' - Destination Binding: workspace-draft', ` - Destination Binding: workspace-draft\n - Relative To Binding: ${relativeTo}`);
|
|
722
|
+
if (relativeMeaning) markdown = markdown.replace(' - Placement Intent:', ` - Relative Placement Meaning: ${relativeMeaning}\n - Placement Intent:`);
|
|
723
|
+
markdown = markdown.replace(' - Explicit Override Allowed: no', ` - Explicit Override Allowed: ${override}`);
|
|
724
|
+
return markdown;
|
|
725
|
+
}
|
|
726
|
+
function placementClosureCase(label, spec, expectedCapable = false, { assertReadable = false } = {}) {
|
|
727
|
+
const id = `tiinex.site.placement-${label}.v1`;
|
|
728
|
+
const markdown = placementVariant(id, spec);
|
|
729
|
+
const preparedCase = prep(sourceTopic, [sourceTopic], { bundledDefinitions: bundled(markdown) });
|
|
730
|
+
const candidate = actionById(preparedCase, id);
|
|
731
|
+
assert.ok(candidate, `${label}: canonical action remains observable`);
|
|
732
|
+
if (assertReadable) {
|
|
733
|
+
assert.equal(candidate.definition?.canonicalReadQualified, true, `${label}: canonical read remains qualified`);
|
|
734
|
+
assert.equal(candidate.resultSemantics?.qualification, 'qualified', `${label}: result truth remains readable/qualified`);
|
|
735
|
+
}
|
|
736
|
+
assert.equal(candidate.productCapable, expectedCapable, `${label}: bounded placement capability`);
|
|
737
|
+
if (!expectedCapable) {
|
|
738
|
+
assert.ok(candidate.capability?.reasons?.includes('unsupported-placement-capability'), `${label}: exact local placement rejection`);
|
|
739
|
+
assertNoMutation(execute({ state: appState([sourceTopic]), currentRecordId: sourceTopic.id, definitionKey: candidate.definitionKey, definitions: bundled(markdown) }), `${label}: unsupported placement declaration`);
|
|
740
|
+
}
|
|
741
|
+
return { markdown, candidate };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Required focused pressure: explicit destination/placement truth is never silently discarded.
|
|
745
|
+
assert.equal(action?.productCapable, true, 'baseline exact destination remains product-capable');
|
|
746
|
+
placementClosureCase('destination-kind', { destinationKind: 'github-repository' }, false, { assertReadable: true });
|
|
747
|
+
placementClosureCase('capability-requirement', { capabilityRequirement: 'remote-write' }, false, { assertReadable: true });
|
|
748
|
+
placementClosureCase('destination-kind-capability', { destinationKind: 'github-repository', capabilityRequirement: 'remote-write' }, false, { assertReadable: true });
|
|
749
|
+
placementClosureCase('relative-meaning', { relativeMeaning: 'Place beside the source Topic.' }, false, { assertReadable: true });
|
|
750
|
+
placementClosureCase('relative-binding', { relativeTo: 'source-topic' }, false);
|
|
751
|
+
placementClosureCase('explicit-naming-reference', { namingReference: '[naming](../naming.md)' }, false, { assertReadable: true });
|
|
752
|
+
assert.equal(success.result.bindingPlan?.qualification, 'qualified', 'baseline fresh v422 remains qualified after placement closure');
|
|
753
|
+
assert.equal(success.result.v423?.qualification, 'qualified', 'baseline fresh v423 remains qualified after placement closure');
|
|
754
|
+
assert.equal(success.adds, 1, 'baseline exact placement still creates exactly one local Task');
|
|
755
|
+
|
|
756
|
+
// Adjacent declaration sweep 1: Required × Destination Kind × Capability Requirement.
|
|
757
|
+
for (const required of ['yes', 'no', 'unknown']) for (const destinationKind of ['', 'arbitrary-kind']) for (const capabilityRequirement of ['', 'arbitrary-capability']) {
|
|
758
|
+
const supported = required === 'yes' && !destinationKind && !capabilityRequirement;
|
|
759
|
+
placementClosureCase(`destination-${required}-${destinationKind ? 'kind' : 'none'}-${capabilityRequirement ? 'cap' : 'none'}`, { required, destinationKind, capabilityRequirement }, supported);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Adjacent declaration sweep 2: Placement Intent × Naming Authority × Naming Authority Reference.
|
|
763
|
+
for (const placementIntent of ['new-materialization', 'no-materialization', 'preserve-current']) for (const namingAuthority of ['explicit-binding', 'target-schema', 'external-authority']) for (const namingReference of ['', '[authority](../authority.md)']) {
|
|
764
|
+
const supported = placementIntent === 'new-materialization' && namingAuthority === 'explicit-binding' && !namingReference;
|
|
765
|
+
placementClosureCase(`placement-${placementIntent}-${namingAuthority}-${namingReference ? 'ref' : 'noref'}`, { placementIntent, namingAuthority, namingReference }, supported);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Adjacent declaration sweep 3: Relative To × Relative Meaning × Explicit Override Allowed.
|
|
769
|
+
for (const relativeTo of ['', 'source-topic']) for (const relativeMeaning of ['', 'Relative placement declaration.']) for (const override of ['no', 'yes', 'unknown']) {
|
|
770
|
+
const supported = !relativeTo && !relativeMeaning && override === 'no';
|
|
771
|
+
placementClosureCase(`relative-${relativeTo ? 'source' : 'none'}-${relativeMeaning ? 'meaning' : 'none'}-${override}`, { relativeTo, relativeMeaning, override }, supported);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
console.log('post-v424 exact local-placement declaration closure + adjacent sweep: PASS');
|