MediaWiki talk:Dialog/receive

From Wikinews, the free news source you can write!
Jump to navigation Jump to search

Instructions for javascript use of this module[edit]

For each dialog action, its page-specific javascript should import this code, as follows. The imported code recovers parameters passed to the action, and passes them to a callback that implements action-specific behavior. It also handles origin authentication, action delegation, restoration of dialog field contents if the user navigates away from a page view and comes back to it, and discarding of least-recently-used per-page-view dialog data.

Anonymous callback[edit]

Use the following pattern.

/* For [[{{#titleparts:{{PAGENAME}}||2}}]]. See [[Special:WhatLinksHere/{{#titleparts:{{PAGENAME}}||2}}]]. */

$.getScript( // [[MediaWiki:Dialog/receive]]
    mw.config.get('wgScript') + '?title=' + mw.util.wikiUrlencode('MediaWiki:Dialog/receive') +
                                '&action=raw&ctype=text/javascript',
    function () {
        window.wikidialog.receiveAnonymous(function () {
            ...
        });
    }
);

The argument to window.wikidialog.receiveAnonymous is called, with this an object whose fields are the data passed to the action by the calling button. The first argument to the callback is an object containing authenticated information about the request for the current action; authentication is explained below. Ordinarily there are no additional arguments to the callback; however, if window.wikidialog.receiveAnonymous takes additional arguments beyond the callback, these are passed on to the callback as additional arguments.

The name "receiveAnonymous" flags out the callback's responsibility to do its own checking and enforcement of any desired incoming authentication constraints.

Outgoing authentication[edit]

To register authentication for action requests from the current page, use either window.wikidialog.proxy or window.wikidialog.purelySelfContained. Use window.wikidialog.proxy if page content is drawn from another page (hereafter, remote page), specifying the name of the other page as an argument to the function; use window.wikidialog.purelySelfContained if all the current page content is local.

Either function first authenticates the origin of the current action request, based on all occurrences of {{dialog/require origin}} or {{dialog/null requirement}}. There must be at least one occurrence of one of these templates, and the conditions set by all such occurrences must be satisfied. window.wikidialog.proxy also takes an optional argument specifying whether or not the remote page is fully protected. (The current page is already known fully protected, since a button targeted it.) If all performed checks succeed, the function registers authentication information to be passed on by requests from the current page. window.wikidialog.proxy also performs a useful function even if authentication fails, registering the name of the remote page so it can be passed on (typically to be made accessible by the recipient via special template parameter REQUESTING-PAGE).

Either function (window.wikidialog.proxy or window.wikidialog.purelySelfContained) takes one or two callbacks as optional trailing arguments, the first of these to be called if and when authentication registration is completed, the second to be called if it fails.

Outgoing action requests authenticated via window.wikidialog.proxy identify the remote page as their origin and the current page as their proxy. Outgoing action requests authenticated via window.wikidialog.purelySelfContained identify the current page as their origin and have no proxy.

The two outgoing authentication request functions, window.wikidialog.proxy and window.wikidialog.purelySelfContained, also indicate that the current url is conducting dialog activity. This may be important metadata for the rollback facility, if the current activity involves dialog state that would need reconstruction but the current url does not imply an incoming dialog request.

Incoming authentication[edit]

When window.wikidialog.receiveAnonymous calls the callback function, it passes as the first argument an object with optional fields origin and proxy. These fields contain authentication information registered for the incoming action request. If no such information was successfully registered for the incoming request, the fields are undefined.

To allow an arbitrary origin but require it to be authenticated, test directly for presence of an origin in the first argument to the callback; the templates do not directly support this limited constraint. {{dialog/require origin}} requires a list of permissible authenticated origins, while {{dialog/null requirement}} does not require that the origin be authenticated.

Dialog field recovery[edit]

An action can make dialog fields on its page robust against navigating away and coming back, by calling window.wikidialog.recover. If any dialog fields are set up by the action, it should set them up before calling recover, since the function only restores values of dialog fields that exist when it is called. The function is also responsible for arranging a handler to save dialog field values when the user navigates away from the page view, so dialog field values will only be navigation-robust if the dialog fields are either specific to an action page that uses recover, or viewed through a proxy action that uses recover. The function is also responsible for discarding least-recently-used dialog-page-view data.

Dialog notes[edit]

Internal notices about an action request that may be of diagnostic interest may be stored by passing each notice to window.wikidialog.setNote; each notice is automatically html-disabled. A mildly formatted concatenation of all current notices can be retrieved via window.wikidialog.getNotes. The current cache of notices can be purged via window.wikidialog.clearNotes. Ordinarily the current cache of notices is purged automatically during delegation, but this automatic purge can be prevented by calling window.wikidialog.keepNotes immediately before window.wikidialog.delegateTo.

For example, this module internally caches an explanatory notice when declining outgoing authentication; and the do action caches notices about its handling of action-sequences. Such information may be meaningless to most users, but valuable to someone trying to debug a dialog.

Dialog notes can be observed through the diagnostic panel, and can also be embedded on a page via {{dialog/notes}} (which is suported by the do action via window.wikidialog.getNotes).

Action delegation[edit]

An action can generate a fresh request to itself modifying the current incoming request, by calling window.wikidialog.delegateTo. All data passed to the current incoming request, including parameters and incoming authentication information, are retained except as overridden.

Basic call format has three arguments: first the primary function of the current action, which is called to produce the action instead of reloading the page; second an object whose fields are dialog data to be modified when delegating; third a function for transforming the action metadata. The final, transforming function takes a single object as argument, with fields unauth, origin, and proxy for the preexisting action's unauthenticated source and authenticated source, and returns an object with these same metadata for the delegation action; fields omitted from the result are understood to be blank. The final, transforming function may be omitted, in which case the metadata is not modified during delegation, but this is not recommended since it doesn't document what is being done; better to provide an explicit identity function, function (metadata) { return metadata; }‍.

An optional fourth metadata field, carry, is an array of names of fields that are "carried over" — that is, explicitly passed unchanged — across the delegation. Ordinarily, the geometry of an input box (its scroll position and size) are reset on delegation; however, geometry of carried-over visible input boxes is preserved.

A variant function, supporting rollback of the delegation in case of low-level failure, is called window.wikidialog.DelegateToWithRollback.

Reserved dialog parameter ACTIVE-PARAMETERS is set by window.wikidialog.DelegateToWithRollback. If not otherwise specified, ACTIVE-PARAMETERS is cleared on non-rollback delegation. Alternatively, window.wikidialog.DelegateToSettingNewparams sets ACTIVE-PARAMETERS, while window.wikidialog.DelegateToPreservingNewparams does not alter it.

Design notes: An early version of the function(s) supported delegation to a different action. The additional functionality caused severe complexity in the implementation of the function, with some remarkably inscrutable code that was a great relief to remove after it became clear that delegation within a single action was the important operation. If delegation to a different action were wanted, the recommended technique would be to construct a hidden button with all the appropriate fields built in and automatically click it (a technique used, as of this writing, by Wikinews:Dialog and Wikinews:Dialog/do).

The default ACTIVE-PARAMETERS behavior on non-rollback delegation is to make no claims, on grounds that default behavior should minimize potential damage. The ACTIVE-PARAMETERS facility has proven occasionally useful in practice, mitigating concerns on whether it was worth the moderately increased complication of the javascript interface.

Geometry is saved only on delegation. Saving geometry on same-page non-delegating button calls would have required duplicating the — rather extensive — geometry-saving code in the gadget, where additional code is discouraged.

Page query[edit]

An action can query information about a page by calling window.wikidialog.pageQuery. A "fields" object passed in to the function indicates fields to be queried: of possible fields supported, only those present in the fields object on input are determined by the query. A callback passed in to the function is called when the query attempt is completed, regardless of whether it succeeds or fails; results of the query attempt are placed in the fields object and it is passed to the callback.

Supported fields for determination are: exists, a boolean value; name, the normalized page name; content and timestamp of the current revision; protected, a boolean indicating whether the page is fully protected against both editing and moving; flagged, either "current", "pending", or "never"; and categories, with the categories delimited by double quotes and separated by spaces.

If query processing fails, field error is set to an error message and no other field values can be relied upon. If field exists is not requested, and the page does not exist, that is an error; if exists is requested but false, no other field values can be relied upon.

Design note: A trial upgrade provided limited caching within this function, but was found to produce scarcely perceptible speedup in exchange for significantly increased code complexity, particularly in the area of cache purging; the upgrade was rejected. Calls to this function are a relatively minor component of time-consumption. Remaining internet accesses are pretty much inherent in what is being done, and the recommended way to achieve major time savings would be to change what is being done, rather than fooling around with details of how it's done (such as caching).

Protection check[edit]

A page can be checked to verify that it exists and is fully protected by calling window.wikidialog.checkProtected. Its first argument is the name of the page to be checked. Its second argument, if provided, is a function to be called if the verification succeeds, hence if the page is verified to exist and be fully protected. Its third argument, if provided, is a function to be called if the verification fails; a string argument to the failure callback describes the cause of failure.

Template parameters substitution[edit]

A wiki markup text can be transformed; substituting for template parameters using given binding for incoming dialog parameters, and processing calls to {{dialog/init}}; by calling window.wikidialog.substituteParameters. Its first argument is the name of the page on which the text is to be treated as if it occurs; second, an object whose fields are the presumed incoming dialog parameters; third, the wiki markup string to be transformed; fourth, a callback to which the transformed string is passed. Alternatively, an optional argument may be inserted between the second and third; see below. There is also a much simpler version of the function available; see below.

Template parameters are ignored if they contain nesting; thus, {{{foo|{{{bar|quux}}}}}} would allow substitution for bar but not for foo.

Two kinds of incoming dialog parameters are handled specially, based on their names. An incoming dialog parameter whose name begins with an upper-case letter and contains no lower-case letters is reserved; one whose name begins with local is local. Before any other processing, reserved and local parameters are deleted from the bindings object provided. Next, certain reserved parameters are added, as detailed below. Then template parameters are substituted for. Finally, {{dialog/init}} calls are processed, ignoring any that specify reserved parameters (but local parameters are fine, and this is the only way local parameters can be provided). The ordering of template parameter substitution and {{dialog/init}} processing can be partially interleaved in certain special cases involving queries with complicated interfaces, detailed below.

Reserved dialog parameters, if required to substitute for template parameters, are provided based on context rather than merely copying from the bindings object provided. REQUESTING-PAGE is assigned the name of the page from which the action request came. INCOMING-AUTHENTICATED is assigned a non-blank value if the incoming action request is authenticated, otherwise it isn't assigned a value. ACTIVE-PARAMETERS is assigned a &-separated and -delimited list of incoming parameters that have just been explicitly passed by a button, distinguishing explicit button action from parameters merely carried along by delegation. USERNAME is assigned the name of the Wikimedia account on which the user is editing, or blank of the user is not logged in. USER-GROUPS is assigned the names of all user groups on the wiki to which the current user belongs, separated by spaces. SUBJECT-EXISTS is assigned true or false depending on existence of the page named by the provided binding of subject; SUBJECT-CONTENT, the raw wiki markup content of that page; SUBJECT-TIMESTAMP, the timestamp of the most recent revision of that page; SUBJECT-CATEGORIES, the names of all categories to which that page belongs; and SUBJECT-FLAGGED, the flaggedrevs status of that page — never, pending, or current. Version-identification strings are also provided for the dialog gadget and for the current module (receive), under names DIALOG-GADGET-VERSION and DIALOG-RECEIVE-VERSION. If the caller provides a value for reserved parameter PRELOAD-PAGENAME (through the optional argument described below), and its value names an existing page, then reserved parameter PRELOAD-CONTENT is assigned the raw wiki markup content of that page, with inclusion directives processed for transclusion. During an ongoing sequence of actions, ACTION-SEQUENCE-BOUND is assigned the number of additional actions permitted in the sequence. If any local parameters were removed from the provided bindings object, their values are provided in reserved parameters, which the name of each local parameter converted to upper case and prefixed with INCOMING-. If the dialog stack is non-empty, reserved parameter STACK-DEPTH is assigned the number of dialog states currently on the stack.

Several sets of reserved parameters are based on over-the-internet queries with complex interfaces, and the complexities are specified using local parameters. To accommodate this arrangement, {{dialog/init}} call processing is done in two tiers: first, after all other template parameters are substituted for, {{dialog/init}} calls are processed that don't involve these query-based reserved template parameters; the queries performed as needed; and finally the remaining {{dialog/init}} calls are processed.

If subject and local-subject-history are both non-blank, revision history information for the page named by subject can be assigned to certain reserved template parameters starting with SUBJECT-HISTORY-. The query can be modulated by local-subject-history-direction and local-subject-history-continue. Information on up to fifty revisions is queried and, if successful, placed into reserved parameters SUBJECT-HISTORY-REVID, SUBJECT-HISTORY-TIMESTAMP, SUBJECT-HISTORY-USER, SUBJECT-HISTORY-MINOR, SUBJECT-HISTORY-SIZE, and SUBJECT-HISTORY-COMMENT; in each reserved parameter, the data are separated by &. If there are more revisions after the ones reported, SUBJECT-HISTORY-CONTINUE contains a value that can be used in subject-history-continue to query further revisions.

If category and local-category-members are both non-blank, members of the category named by category can be assigned to certain reserved template parameters starting with CATEGORY-MEMBERS-. The query can be modulated by local-category-members-direction, local-category-members-type, local-category-members-sort, and local-category-members-continue. Information on up to fifty members is queried and, if successful, placed into reserved parameters CATEGORY-MEMBERS-TITLE, CATEGORY-MEMBERS-TYPE, and CATEGORY-MEMBERS-TIMESTAMP; in each reserved parameter, the data are separated by &. If there are more members after the ones reported, CATEGORY-MEMBERS-CONTINUE contains a value that can be used in category-members-continue to query further members.

If local-text-to-expand is non-blank, its template-expansion is assigned on demand to reserved template parameter EXPANDED-TEXT.

If file and local-file-info are both non-blank, file information about the file named by file can be assigned to reserved template parameters starting with FILE-INFO-. Basic information on the size of the unscaled image is provided in reserved template parameters FILE-INFO-SIZE, FILE-INFO-WIDTH, and FILE-INFO-HEIGHT. File metadata can also be queried through reserved template parameters starting with FILE-INFO-META-, as provided by the API through iiprop=extmetadata; as of this writing, these metadata fields aren't well documented, but see mw:API:Imageinfo.

An optional extra argument may be inserted between the second and third; it should be an object, formatted as the fields argument to window.wikidialog.pageQuery, requesting data regardless of whether the corresponding template parameters occur. A field content requests parameter SUBJECT-CONTENT, and so on. The extra argument can also be used to specify additional reserved parameter assignments; any binding of a reserved parameter in the extra argument is copied into the incoming bindings. Typically a version-identification string is provided for the calling action, such as DIALOG-DO-VERSION.

A much simpler transformation is available by calling window.wikidialog.substituteTemplateParameters. Its first argument is an object whose fields are the presumed incoming dialog parameters, and its second argument is the wiki markup string to be transformed. The specified parameters are substituted for template parameters in the wiki markup string.

Design notes: Because fetching a preload page is expensive, a pseudo-template was considered as an inexpensive alternative, preprocessed (similarly to {{dialog/init}}) to return its template argument unexpanded. However, the pseudo-template would put the raw material in the dialog page itself, where it could not be unprotected without unprotecting the dialog; and its use would be fragile since the scope of the pseudo-template call would be delimited by double-braces. Given these limitations, the pseudo-template was deemed not worth the additional interface complexity.

The interleaved processing of {{dialog/init}} calls and reserved template parameters was approached cautiously, to avoid creating political pressures for future progressive loss of simplicity, and thus useability and maintainability. To this end, the interleaving was minimized commensurate with certain specific objectives: (1) a complex API interface with the same look-and-feel as the complex API interface of the do verb edit; (2) queries controlled strictly by the local markup of the page itself, as would be true of template parameters alone; and (3) ability to channel all template parameters through {{dialog/init}} for unformatted use in initializing text input fields. The only inter-parameter dependencies introduces by the interleaving are strictly required for these objectives.

Function window.wikidialog.substituteTemplateParameters supports substitution of dialog parameters for template parameters in preview, design for which is discussed at Template:Dialog/preview/doc#Internals.

Html safety[edit]

A string can be rendered safe for insertion in html via function window.wikidialog.safeHtml. Its argument is the string to be sanitized, and its result is a "safe" string; angle brackets in the string are replaced by < and >, thereby preventing surreptitious insertion of html tags. This is an important safety measure when arbitrary strings (usually, dialog parameters) are inserted directly into html for display on the page, which is commonly done when displaying error messages. Most non-error cases are already safe because material inserted into the page (by, for example, the do action) has been output by the wiki parser, which produces safe html.

For example, one might have

showError('Target ' + window.wikidialog.safeHtml(actionParams.target) + ' does not exist');

Inclusion directive processing[edit]

Raw wiki markup can contain inclusion directives — noinclude, includeonly, onlyinclude — modulating which parts apply when the markup is transcluded to another page versus which apply when the markup occurs directly on the local page. Function window.wikidialog.transclude processes these directives to produce just the parts that apply when transcluded on another page. Its one argument is the raw wiki markup to be processed. It does no other processing besides inclusion directives, stopping short of template expansion (which would require a server call and would therefore be an expensive operation, whereas merely processing inclusion directives can be done client-side).

Incoming action request validation[edit]

Function window.wikidialog.validIncoming() returns a boolean indicating whether or not the page has been arrived at through a dialog action request. It is used internally by window.wikidialog.receiveAnonymous to decide whether the dialog action page has actually been invoked or is merely being viewed. It is also useful for determining whether or not query parameters embedded in the url should be converted into a dialog action request; see action do url conversion and the standalone url conversion action.

Internally, each action request (within a logical browser session) has a unique ID number, which is used to store data associated with that request, such as incoming dialog parameters. Such internal storage is encapsulated within the dialog gadget, the current module (receive), and necessarily the diagnostic panel. Ordinarily, the ID number is embedded in the url of the action request; naturally, it doesn't change when delegating an action request within the same action. The dialog tools are designed primarily to work with the ID number embedded in the url, and the standalone url conversion action works by jumping to a fresh url with an embedded ID. However, action do can also operate directly with a url that has query parameters but no embedded ID. Without the embedded ID, navigating away from the action request and then naively back to it will usually cause the software to lose track of what ID it was using, so that the earlier dialog data cannot be recovered (except perhaps laboriously through the diagnostic panel). The rollback facility should be able to recover dialog state from a url without embedded ID, if the activity was flagged by use of an outgoing authentication function; in such a case, the rollback facility assumes that the unembedded ID was one less than the next ID maintained by the dialog software.

Metadata stored under each dialog ID includes the url of the action request associated with that ID; that metadata is stored by a button when clicked (regardless of whether the button is delegating), so its presence under the current dialog ID indicates that the current action was initiated by a button click, and this is what function window.wikidialog.validIncoming() checks for.

Rollback[edit]

Special facilities support rollback of an action request to the previous dialog state from which the request was issued. Rollback is intended primarily to avoid catastrophic data loss due to a low-level (API) failure, such as inability to read an edit form or inability to perform the low-level edit operation initiated by a mediating edit form. Rollback is not primarily envisioned as a response to a dialog-level logical decision, such as when an edit form loads correctly but then refuses to initiate the requested edit because of some condition specified on the form; those sorts of error should preferentially be handled within the dialog framework, although rollback may be a last resort for some situations whose handling is fumbled in-dialog. (However, rollback does also form the technical medium for view confirmation.)

Rollback may be "simple", of a request from a different url; or "delegating", of a delegated request. To determine whether or not rollback is currently possible, call window.wikidialog.canRollback, which returns a boolean.

Rollback when possible is initiated by calling window.wikidialog.rollback, which returns a boolean indicating whether rollback appears to have been successfully initiated; a function argument must be provided suitable as the first argument to window.wikidialog.delegateTo (although this argument is actually used only for delegating rollback). Rollback is disabled by calling window.wikidialog.commit, which erases the rollback data; or disabled when a further action request is issued.

A simple request can be rolled back unless the requesting button used {{dialog/button}} template parameter norollback. A delegating request can be rolled back only if the requester performed delegation by calling alternative function window.wikidilaog.delegateToWithRollback.

Design note: Robust maintainability of the software calls for simple, error-retardant logic.
  • Rollback data is kept separately for the two cases, simple and delegating. Both are kept strictly separate from all other stored values (such as dialog field recovery data; although when actually rolling back, field values are restored by feeding them into the dialog field recovery mechanism).
  • Each of the two kinds of rollback data is only written by a single function: simple-rollback data only by the button handler of the gadget, delegating-rollback data only by internal function delegateToInternal, whose public interface is through functions window.wikidialog.delegateTo and window.wikidialog.delegateToWithRollback.
  • delegateToInternal takes responsibility for removing simple-rollback data as appropriate. However, the gadget's only provision for rollback is storing the simple-rollback data (which only takes a single line); this keeps the delegating-rollback data handling entirely encapsulated within the receive module. To preserve consistency, in all rollback-related code in the receive module that doesn't erase simple-rollback data, simple-rollback data disables delegating rollback and causes erasure of delegating-rollback data.

Stack[edit]

A dialog state can be saved and come back to later, using functions window.wikidialog.pushPrevious and window.wikidialog.popUrl. These states are preserved for as long as they're on the stack, separately from the set of most-recently-used states maintained by function window.wikidialog.recover. As of this writing, the maximum stack size is half the size of recover's most-recently-used set (changing automatically if the later size parameter is adjusted).

Function window.wikidialog.pushPrevious attempts to save the previous dialog state onto the stack. It can only do so if rollback to the previous state is still possible, the state change from previous to current state was non-delegating, and the stack is not yet full. If the function fails to push the previous state, it returns a string explaining why.

Function window.wikidialog.popUrl attempts to remove the topmost dialog state from the stack and resume it. It returns true if successful, false if unsuccessful.

Design note: This is an extremely straightforward-to-implement facility that, amongst other possible uses, empowers wiki contributors to provide meta-dialog facilities: while using a dialog one might suspend it to enter a meta-dialog for modifying the dialog one was just using, then pop back into the dialog having modified it. All the state saving and restoring are handled by other facilities already in place; only the id of the previous state (to prevent it from being deallocated) and its url (to jump to when popping) need to be recorded on the stack. In contrast, if one wanted to support pushing a delegated state, the implementation would be enormously complicated with no other facilities to help, and would introduce lots of strongly coupling with other mechanisms; the degradation of simplicity of implementation is certainly not worth the benefit given that an alternative solution for push/pop is available, and probably wouldn't be worth the benefit even if there weren't an alternative solution available.

Dialog installation[edit]

Installation of the dialog software on a wikimedia project involves the following elements. Some internal details of the software were not initially written with full project-generality built into them; pockets not yet generalized may remain. Ancestral Category:Dialog infrastructure attempts to gather together everything needed (barring mundane general wiki infrastructure — things like template {{tl}}). The tools currently assume that the name of the local project space is {{SITENAME}}: (for example, here it is Wikinews:). When importing each page, check for associated documentation and subpages, adjust for local differences in categorization (customizing {{dialog/subtemplates cat}} takes care of much of that), and impose suitable page protections (sometimes tricky). Assistants are arranged under a separate ancestor, Category:Interactive assistants, with some overlap.

This is a very small surgical change, but profoundly transforms the role of javascript in project customization because, in essence, it allows certain select wiki pages to be verbs rather than nouns. Page-specific javascript won't impact load time for any wiki page other than the one it's specific to, and will work for any user, even an IP. Naming conventions for page-specific javascript may have to vary between projects due to possible conflicts; these variations should be handled on ordinary wiki pages by {{dialog/js prefix}}.
Recommended this be at the top of MediaWiki:Gadgets-definition; make it opt-in at first by omitting the default option, then once the installation is satisfactorily complete and working correctly make it opt-out. The gadget is the only substantial dialog javascript that is run unconditionally, rather than page-specifically. It causes dialog elements to be visible on the page, and enables dialog buttons on the page so they can make dialog-field content available for use by page-specific javascript.
This is provided for use by page-specific javascript, to access data passed through a button by the dialog gadget. Also provides a number of related facilities to help clients remain independent of internal details of dialog data-passing implementation.
These embed information on a wiki page for use by the dialog gadget and receive module. The user perceives these templates as "doing" much of the work of the dialog software, because all instructions to the dialog gadget and receive module are encoded using these templates, even though internally the templates are "just" cosmetic.
As of this writing, one multi-purpose action is provided for doing pretty much everything, because moving from one action to another is expensive (it's expected to add about one second to access time). There are two specialized action-like devices — diagnostic panel and url-to-dialog converter — and some legacy actions retained as illustrations. A minimal test case is action xyzzy.
This complements the primitive dialog software, by providing powerful facilities to succinctly manipulate the contents of wiki pages from within wiki markup, so such manipulations are available for use in interactive dialogs.
These are sets of pages that use dialog to aid human users in performing various tasks; essentially, wizards. Although many assistants may be specific to a particular project, some are expected to be of interest for any project at all (for example, assistants for building and maintaining assistants).

Recommended miscellany: