From 8747cc9f94e1f1ad545cbe7e2c26c7d46b67af74 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 5 Mar 2022 23:17:51 +0000 Subject: [PATCH] Import design docs (RFCs) --- docs/index.md | 19 + docs/rfcs/_index.md | 24 ++ docs/rfcs/cli.md | 162 +++++++++ docs/rfcs/client.md | 444 +++++++++++++++++++++++ docs/rfcs/dom.md | 259 ++++++++++++++ docs/rfcs/plans.md | 436 +++++++++++++++++++++++ docs/rfcs/protocol.md | 211 +++++++++++ docs/rfcs/recurrence.md | 203 +++++++++++ docs/rfcs/request.md | 221 ++++++++++++ docs/rfcs/rules.md | 231 ++++++++++++ docs/rfcs/sync.md | 253 +++++++++++++ docs/rfcs/task.md | 761 ++++++++++++++++++++++++++++++++++++++++ docs/rfcs/week.png | Bin 0 -> 11318 bytes docs/rfcs/workweek.md | 35 ++ docs/rfcs/year.png | Bin 0 -> 15455 bytes 15 files changed, 3259 insertions(+) create mode 100644 docs/rfcs/_index.md create mode 100644 docs/rfcs/cli.md create mode 100644 docs/rfcs/client.md create mode 100644 docs/rfcs/dom.md create mode 100644 docs/rfcs/plans.md create mode 100644 docs/rfcs/protocol.md create mode 100644 docs/rfcs/recurrence.md create mode 100644 docs/rfcs/request.md create mode 100644 docs/rfcs/rules.md create mode 100644 docs/rfcs/sync.md create mode 100644 docs/rfcs/task.md create mode 100644 docs/rfcs/week.png create mode 100644 docs/rfcs/workweek.md create mode 100644 docs/rfcs/year.png diff --git a/docs/index.md b/docs/index.md index e4f92ab76..d9850f408 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,3 +13,22 @@ For all other documenation, see https://taskwarrior.org. * [Building Taskwarrior](./contrib/build) * [Coding Style](./contrib/coding_style) * [Branching Model](./contrib/branching) + +## RFC's + +This is where design documents (RFCs) are kept. + +Although these documents are less formal than [IETF RFCs](https://www.ietf.org/rfc) they serve a similar purpose. +These documents apply only to the Taskwarrior family of products, and are placed here to invite comment before designs finalize. + + - [General Plans](./rfcs/plans) + - [Rules System](./rfcs/rules) + - [Full DOM Support ](./rfcs/dom) + - [Work Week Support](./rfcs/workweek) + - [Recurrence](./rfcs/recurrence) + - [Taskwarrior JSON Format](./rfcs/task) + - [CLI Updates ](./rfcs/cli) + - [Taskserver Sync Protocol](./rfcs/protocol) + - [Taskserver Message Format](./rfcs/request) + - [Taskserver Sync Algorithm](./rfcs/sync) + - [Taskserver Client](./rfcs/client) diff --git a/docs/rfcs/_index.md b/docs/rfcs/_index.md new file mode 100644 index 000000000..4ecb6a2bc --- /dev/null +++ b/docs/rfcs/_index.md @@ -0,0 +1,24 @@ +--- +title: "Taskwarrior - What's next?" +--- + +# Design + +## Plans + + - [Task warrior/Taskserver/Tasksh/Timewarrior Plans](/docs/design/plans) + +## RFCs + + +### Tasksh + + - (No announced plans) + +### Timewarrior + + - (No announced plans) + +### Upcoming + + - Lazy Dates (Deferred Evaluation) diff --git a/docs/rfcs/cli.md b/docs/rfcs/cli.md new file mode 100644 index 000000000..99fd8b3c4 --- /dev/null +++ b/docs/rfcs/cli.md @@ -0,0 +1,162 @@ +--- +title: "Taskwarrior - Command Line Interface" +--- + +## Work in Progress + +This design document is a work in progress, and subject to change. Once +finalized, the feature will be scheduled for an upcoming release. + + +# CLI Syntax Update + +The Taskwarrior command line syntax is being updated to allow more consistent +and predictable results, while making room for new features. + +Adding support for arbitrary expressions on the command line has become +complicated because of the relaxed syntax of Taskwarrior. While the relaxed +syntax allows for a very expressive command line, it also creates ambiguity for +the parser, which needs to be reduced. + +With some limited and careful changes it will be possible to have a clear and +unambiguous command line syntax, which means a predictable and deterministic +experience. + +It should be stated that for straightforward and even current usage patterns, +the command line will likely not change for you. Another goal is to not require +changes to 3rd-party software, where possible. Only the more advanced and as-yet +unintroduced features will require a more strict syntax. This is why now is an +ideal time to tighten the requirements. + + +## Argument Types + +The argument types supported remain the same, adding some new constructs. + + --------------------------------------- --------------------------------------- + Config file override `rc:` + + Configuration override `rc::` Literal value\ + `rc:=` Literal value\ + `rc::=` Calculated value + + Tag `+`\ + `-`\ + `'+tag one'` Multi-word tag + + Attribute modifier `rc:.:`\ + Modifier is one of:\ + `before`\ + `after`\ + `under`\ + `over`\ + `above`\ + `below`\ + `none`\ + `any`\ + `is`\ + `isnt`\ + `equals`\ + `not`\ + `contains`\ + `has`\ + `hasnt`\ + `left`\ + `right`\ + `startswith`\ + `endswith`\ + `word`\ + `noword` + + Search pattern `//` + + Substitution `///`\ + `///g` + + Command `add`\ + `done`\ + `delete`\ + `list`\ + etc. + + Separator `--` + + ID Ranges `[-<id>][,[-<id>]...]` + + UUID `` + + Everything Else ``\ + `' ...'` + --------------------------------------- --------------------------------------- + + +## New Command Line Rules + +Certain command line constructs will no longer be supported, and this is imposed +by the new rules: + +1. Each command line argument may contain only one instance of one argument + type, unless that type is ``. + + task add project:Home +tag Repair the thing # Good + task add project:Home +tag 'Repair the thing' # Good + task add 'project:Home +tag Repair the thing' # Bad + + Putting two arguments into one quoted arg makes that arg a ``. + +2. If an argument type contains spaces, it must either be quoted or escaped. + + task add project:'Home & Garden' ... # Good + task add 'project:Home & Garden' ... # Good + task add project:Home\ \&\ Garden ... # Good + task add project:Home' & 'Garden ... # Good + task add project:Home \& Garden ... # Bad + + The parser will not combine multiple arguments, for example: + + task '/one two/' list # Good + task /one two/ list # Bad + task /'one two'/ list # Bad, unless ' is part of the pattern + +3. By default, *no* calculations are made, unless the `:=` eval operator is + used, and if so, the whole argument may need to be quoted or escaped to + satisfy Rule 1. + + task add project:3.project+x # Literal + task add project:=3.project+x # DOM reference + concatenation + +4. Bare word search terms are no longer supported. Use the pattern type + argument instead. + + task /foo/ list # Good + task foo list # Bad + +5. Expressions must be a series of arguments, not a quoted string. + + task urgency \< 5.0 list # Good + task 'urgency < 5.0 list' # Bad + + +## Other Changes + +Aside from the command line parser, there are other changes needed: + +- Many online documents will need to be modified. + +- Filters will be automatically parenthesized, so that every command line will + now looke like: + + task [overrides] [(cli-filter)] [(context-filter)] [(report-filter)] command [modifications] + +- There will be more errors when the command line is not understood. + +- Ambiguous ISO date formats are dropped. + + YYYYMMDD # Bad + YYYY-MM-DD # Good + + hhmmss # Bad + hh:mm:ss # Good + +- The tutorial videos will be even more out of date, and will be replaced by a + large number of smaller demo \'movies\'. diff --git a/docs/rfcs/client.md b/docs/rfcs/client.md new file mode 100644 index 000000000..0d018f15a --- /dev/null +++ b/docs/rfcs/client.md @@ -0,0 +1,444 @@ +--- +title: "Taskwarrior - Creating a Taskserver Client" +--- + + +# Creating a Taskserver Client + +A Taskserver client is a todo-list manager. It may be as simple as a program +that captures a single task, as complex as Taskwarrior, or anything in between. +It can be a mobile client, a web application, or any other type of program. + +This document describes how such a client would interact with the server. + +A client to the Taskserver is a program that manages a task list, and wishes to +exchange data with the server so that the task list may be shared. + +In order to do this, a client must store tasks locally, upload local changes, +download remote changes, and apply remote changes to the local tasks. + +The client must consider that there may be no network connectivity, or no desire +by the user to synchronize. + +The client will need proper credentials to talk to the server. + + +## Requirements + +In this document, we adopt the convention discussed in Section 1.3.2 of +[RFC1122](https://tools.ietf.org/html/rfc1122#page-16) of using the capitalized +words MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL to define the +significance of each particular requirement specified in this document. + +In brief: \"MUST\" (or \"REQUIRED\") means that the item is an absolute +requirement of the specification; \"SHOULD\" (or \"RECOMMENDED\") means there +may exist valid reasons for ignoring this item, but the full implications should +be understood before doing so; and \"MAY\" (or \"OPTIONAL\") means that this +item is optional, and may be omitted without careful consideration. + + +## Taskserver Account + +A Taskserver account must be created. This process creates a storage area, and +generates the necessary credentials. + + +## Credentials + +A Taskserver client needs the following credentials in order to communicate with +a server: + +- Server address and port +- Organization name +- User name +- Password +- Certificate +- Key + +The server address and port are the network location of the server. An example +of this value is: + + foo.example.com:53589 + +In addition to a DNS name, this can be an IPv4 or IPv6 address. + +The organization name is an arbitrary grouping, and is typically \'PUBLIC\', +reflecting the individual nature of server accounts. Future capabilities will +provide functionality that support groups of users, called an organization. + +The user name is the full name. This will be the name used to identify other +users in an organization, in a future release. Example \'John Doe\'. + +The password is a text string generated by the server at account creation time. +It should be considered a secret. + +The certificate is an X.509 PEM file generated by the server at account creation +time. This is used for authentication. It should be considered a secret. + +The key is an X.509 PEM file generated by the server at account creation time. +This is used for encryption. It should be considered a secret. + +These credentials need to be stored on the client, and used during the sync +operation. + + +## Description of a Taskserver Client + +This section describes how a client might behave in order to facilitate +integration with the Taskserver. + + +## Encryption + +The Taskserver only communicates using encryption. Therefore all user data is +encrypted while in transit. The Taskserver currently uses +[GnuTLS](https://gnutls.org) to support this encryption, and therefore supports +the following protocols: + +- SSL 3.0 +- TLS 1.0 +- TLS 1.1 +- TLS 1.2 + +The client may use any library that supports the above. + + +## Configuration + +The client needs to store configuration, which matches the credentials needed +for Taskserver communication. See section 2.1 \"Credentials\". + +The credentials may not be modified by the user without losing server access. + +The server:port data may need to be changed automatically following a redirect +response from the server. See section 5 \"Server Errors\". + + +## Local Storage + +The client needs to store task data locally. The client will need to be able to +find tasks by their UUID and overwrite them. Uploaded and downloaded task +changes will use the [Taskwarrior Data Interchange +Format](/docs/design/task). + + +## Local Changes + +Whenever local data is modified, that change MUST be synced with the server. But +this does not have to occur immediately, in fact the client SHOULD NOT assume +connectivity at any time. + +A client SHOULD NOT also assume that the server is available. If the server is +not available, the local changes should be retained, and the sync operation +repeated later. + +Ideally the client will give the user full control over sync operations. +Automatically syncing after all local modifications is not recommended. If a +client performs too many sync operations, the server MAY revoke the certificate. + +Effectively, the client should maintain a separate list of tasks changed since +the last successful sync operation. + +Note that tasks have a \"modified\" attribute, which should be updated whenever +a change is made. This attribute contributes to conflict resolution on the +server. + + +## Remote Changes + +When a server sends remote changes to a client, in the response to a sync +request, the changes have already been merged by the server, and therefore the +client should simply store them intact. + +Based on the UUID in the task, the client can determine whether a task is new +(and should be added to the local list of tasks), or whether it represents a +modification (and should overwrite it\'s existing entry). + +The client MUST NOT perform any merges. + + +## Sync Key + +Whenever a sync is performed, the server responds by sending a sync key and any +remote changes. The sync key is important, and should be included in the next +sync request. The client is REQUIRED to store the sync key in every server +response message. + +If a client omits the sync key in a sync message, the response will be a +complete set of all tasks and modifications. + + +## Data Integrity + +Although a task is guaranteed to contain at least \'entry\', \'description\' and +\'uuid\' attributes, it may also contain other known fields, and unknown +user-defined fields. An example might be an attribute named \'estimate\'. + +If a task is received via sync that contains an attribute named \'estimate\', +then a client has the responsibility of preserving the attribute intact. If that +data is shown, then it is assumed to be of type \'string\', which is the format +used by JSON for all values. + +Conversely, if a client wishes to add a custom attribute, it is guaranteed that +the server and other clients will preserve that attribute. + +Using this rule, two clients of differing capabilities can exchange data and +still maintain custom attributes. + +This is a requirement. Any client that does not obey this requirement is broken. + + +## Synchronizing + +Synchronizing with the Taskserver consists of a single transaction. Once an +encrypted connection is made with the server, the client MUST compose a [sync +request message](/docs/design/request). This message includes credentials +and local changes. The response message contains status and remote changes, +which MUST be stored locally. + + +## Establishing Encrypted Connection + +All communication with the Taskserver is encrypted using the certificate and key +provided to each user. Using the \'server\' configuration setting, establish a +connection. + + +## Sync Request + +See [sync request message](/docs/design/request). A sync request MUST +contain a sync key if one was provided by a previous sync. A sync request MUST +contain a list of modified tasks, in JSON format (see [Task +JSON](/docs/design/task)), if local modifications have been made. + + +## Sync Response + +A sync response WILL contain a \'code\' and \'status\' header variable, WILL +contain a sync key in the payload, and MAY contain a list of tasks from the +server in JSON format (see [Task JSON](/docs/design/task)). + + +## Server Messages + +There are cases when the server needs to inform the user of some condition. This +may be anticipated server downtime, for example. The response message is +typically not present, but may be present in the header, containing a string: + + ... + message: Scheduled maintenance 2013-07-14 08:00UTC for 10 minutes. + ... + +If such a message is returned by the server, it SHOULD be made available to the +user. This is a recommendation, not a requirement. + + +## Server Errors + +The server may generate many errors (See +[Protocol](/docs/design/protocol)), but the following is a list of the ones +most in need of special handling: + +- 200 Success +- 201 No change +- 301 Redirect +- 430 Access denied +- 431 Account suspended +- 432 Account terminated +- 5xx Error + +The 200 indicates success, and that a change was recorded. The 201 indicates +success but no changes were necessary. The 301 is a redirect message indicating +that the client MUST re-request from a new server. The 43x series messages are +account-related. Any 5xx series code is a server error of some kind. All errors +consist of a code and a status message: + + code: 200 + status: Success + + +## Examples + +Here are examples of properly formatted request and response messages. Note that +the messages are indented for clarity in this document, but is not the case in a +properly formatted message. Also note that newline characters U+000D are not +shown, but are implied by the separate lines. Because some messages have +trailing newline characters, the text is delimited by the \'cut\' markers: + + foo + +The example above illustrates text consisting of: + + U+0066 f + U+006F o + U+006F o + U+000D newline + U+000D newline + +Note that these values are left unspecified, but should be clear from the +context, and the [message format](/docs/design/request) spec: + + + + + + + +## First Sync + +The first time a client syncs, there is (perhaps) no data to upload, and no sync +key from a previous sync. + + type: sync + org: + user: + key: + client: task 2.3.0 + protocol: v1 + +Note the double newline character separating header from payload, with an empty +payload. + + +## Request: Sync No Data + +Ordinarily when a client syncs, there is a sync key from the previous sync +response to send. This example shows a sync with no local changes, but a sync +key from a previous sync. + + type: sync + org: + user: + key: + client: task 2.3.0 + protocol: v1 + + 2e4685f8-34bc-4f9b-b7ed-399388e182e1 + + +## Request: Sync Data + +This sync request shows a sync key from the previous sync, and a locally +modified task. + + type: sync + org: + user: + key: + client: task 2.3.0 + protocol: v1 + + 2e4685f8-34bc-4f9b-b7ed-399388e182e1 + {"description":"An example","uuid":"8ad2e3db-914d-4832-b0e6-72fa04f6e331",...} + + +## Response: No Data + +If a sync results in no downloads to the client, the response will look like +this. + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 200 + status: Ok + + 45da7110-1bcc-4318-d33e-12267a774e0f + +Note that there is a sync key which must be stored and used in the next sync +request, but there are no remote changes to store. + + +## Response: Remote Data + +This shows a sync response providing a new sync key, and a remote change to two +tasks. + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 200 + status: Ok + + 45da7110-1bcc-4318-d33e-12267a774e0f + {"description":"Test data","uuid":"8ad2e3db-914d-4832-b0e6-72fa04f6e331",...} + {"description":"Test data2","uuid":"3b6218f9-726a-44fc-aa63-889ff52be442",...} + +Note that the sync key must be stored for the next sync request. + +Note that the two changed tasks must be stored locally, and if the UUID in the +tasks matches local tasks, then the local tasks must be overwritten. + + +## Response: Error + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 431 + status: Account suspended + +Note the double newline character separating header from payload, with an empty +payload. + + +## Response: Relocate + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 301 + status: Redirect + info: + +Note the \'info\' field will contain a \':\' string that should be used for all +future sync requests. This indicates that a user account was moved to another +server. + +Note the double newline character separating header from payload, with an empty +payload. + + +## Response: Message + +Occasionally the server will need to convey a message, and will include an +additional header variable containing that message. + +The server [protocol](/docs/design/protocol) states that the message SHOULD +be shown to the user. This message will be used for system event messages, used +rarely, and never used for advertising or promotion. + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 200 + status: Ok + message: Scheduled maintenance 2013-07-14 08:00UTC for 10 minutes. + + 45da7110-1bcc-4318-d33e-12267a774e0f + +Note that the same message will likely be included in consecutive responses. + + +## Reference Implementation + +The Taskserver 1.1.0 codebase contains a reference implementation of an SSL/TLS +client and server program, which communicate text strings. + + taskd.git/src/tls/Makefile # To build the example + taskd.git/src/tls/README # How to run the example + taskd.git/src/tls/TLSClient.cpp # TLS client code + taskd.git/src/tls/TLSClient.h + taskd.git/src/tls/TLSServer.cpp # TLS Server code + taskd.git/src/tls/TLSServer.h + taskd.git/src/tls/c.cpp # Client program + taskd.git/src/tls/s.cpp # Server program + taskd.git/src/tls/text.cpp # Text manipulation + taskd.git/src/tls/text.h # Text manipulation + +The Taskwarrior codebase, version 2.4.0, is the reference implementation. + + task.git/src/TLSClient.cpp # TLS client code + task.git/src/TLSClient.h + task.git/src/commands/CmdSync.cpp # Sync implementation + task.git/src/commands/CmdSync.h diff --git a/docs/rfcs/dom.md b/docs/rfcs/dom.md new file mode 100644 index 000000000..d63bc5c57 --- /dev/null +++ b/docs/rfcs/dom.md @@ -0,0 +1,259 @@ +--- +title: "Taskwarrior - Full DOM Support" +--- + +## Work in Progress + +This design document is a work in progress, and subject to change. Once +finalized, the feature will be scheduled for an upcoming release. + + +# Full DOM Support + +Taskwarrior currently supports DOM references that can access any stored data +item. The general forms supported are: + + [ | ] [ ] + +Examples include: + + due + 123.uuid + entry.month + 123.annotations.0.entry.year + a87bc10f-931b-4558-a44a-e901a77db011.description + +Additionally there are references for accessing configuration and system/program +level items. + + rc. + context.program + context.args + context.width + context.height + system.version + system.os + +While this is adequate for data retrieval, we have the possibility of extending +it further to include data formats, higher-level constructs, and then to make +use of DOM references in more locations. This contributes to our goal of +simplifying Taskwarrior. + + +## Proposed Format Support + +When defining a custom report, the columns shown are defined like this: + + report.x.columns=uuid.short,description.oneline ... + +This syntax is: + + [ . ] + +If no `format` is specified, then `default` is assumed. The src/columns/ColΧ\* +objects are responsible for supporting and rendering these formats. There is +currently no consistency among these formats based on data type. + +By incorporating formats into DOM references, we eliminate the need for a +separate syntax for custom reports, and provide this: + + 123.due.iso + 123.due.month.short + 123.uuid.short + +A standard set of formats per data type would be: + +Type + +Formats + +Example + +Numeric + +default + +`123 ` + +indicator + +Based on `rc..indicator` which overrides `rc.numeric.indicator`. + +json + +`"":""` + +String + +default + +Buy milk + +short + +Feb + +indicator + +Based on `rc..indicator` which overrides `rc.string.indicator`. + +json + +`"":""` + +Date + +default + +Based on `rc.dateformat` + +iso + +2017-02-20T09:02:12 + +julian + +2457805.12858 + +epoch + +1234567890 + +age + +2min + +relative + +-2min + +remaining + +0:02:04 + +countdown + +0:02:04 + +indicator + +Based on `rc..indicator` which overrides `rc.date.indicator`. + +json + +`"":""` + +Duration + +default + +1wk + +iso + +P1W + +indicator + +Based on `rc..indicator` which overrides `rc.duration.indicator`. + +json + +`"":""` + +There will also be a set of attribute-specific formats, similar to the currently +supported set: + + depends.list + depends.count + description.combined + description.desc + description.oneline + description.truncated + description.count + description.truncated_count + parent.default|long + parent.short + project.full + project.parent + project.indented + status.default|long + status.short + tags.default|list + tags.count + urgency.default|real + urgency.integer + uuid.default|long + uuid.short + +Custom report sort criteria will also use DOM references. This will be augmented +by the `+`/`-` sort direction and `/` break indicator, which are not part of the +DOM. + + +## High Level Construct Support + +There need to be read-only DOM references that do not correspond directly to +stored attributes. Tasks have emergent properties represented by virtual tags, +which will be accessible, in this case returning a `0` or `1`: + + 123.tags.OVERDUE + +Using `rc.due` and the `due` attribute, the `OVERDUE` virtual tag is a +combination of the two. Other examples may include: + + task.syncneeded + task.pending.count + task.hooks.installed + + +## Writable References + +When a DOM reference refers to an attribute or RC setting, and does not extend +further and reference a component or format, it may be writable. For example: + + rc.hooks # writable + 123.description # writable + 123.entry.month # not writable, not an attribute + + +## Data Interchange + +The export command can be used to show a filtered set of tasks in JSON format, +and this will also be available as a DOM format: + + 123.json + a87bc10f-931b-4558-a44a-e901a77db011.json + + +## RC File Support + +The RC file (`~/.taskrc`) will support DOM references in values. This will form +a late-bound reference, which is evaluated at runtime, every time. + +An example is to make two reports share the same description: + + $ task config -- report.ls.description rc.report.list.description + +This sets the description for the `ls` report to be a reference to the +description of the `list` report. This reference is not evaluated when the entry +is written, but is evaluated every time the value is read, thus providing +late-bound behavior. Then if the description of the `list` report changes, so +does that of the `ls` report automatically. + + +## Implementation Details + +These notes list a series of anticipated changes to the codebase. + +- The `src/columns/Col*` objects will implement type-specific and + attribute-specific DOM support. DOM reference lookup will defer to the + column objects first. +- Some DOM references will be writable, permitting a `_set` command to + complement the `_get` command. +- The `Config` object will recognize DOM references in values and perform + lookup at read time. This will require circularity detection. +- `src/DOM.cpp` will provide a memoized function to determine whether a DOM + reference is valid. +- `src/DOM.cpp` will provide a function to obtain a DOM reference value, with + supporting metadata (type, writable). diff --git a/docs/rfcs/plans.md b/docs/rfcs/plans.md new file mode 100644 index 000000000..dc056aff9 --- /dev/null +++ b/docs/rfcs/plans.md @@ -0,0 +1,436 @@ +--- +title: "Plans" +--- + +There are many interconnected features and technologies in Taskwarrior, +Taskserver, Tasksh and Timewarrior, each piece having it's own goals. + +This matrix allows a simple reading of where things are, and where they are +going. This is a low-resolution time line. It is subject to change. It does not +constitute a concrete plan. This is an all-volunteer effort, and scheduling is +difficult. + +[Last updated 2016-08-08.] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Taskwarrior
Technology/Feature
+ 2.5.1
+ Current

+ Released 2016-02-24 +
+ 2.6.0
+ Next

+ 2017 +
+ 2.x
+ Future +
Core + DOM
+ Filters
+ Expressions
+ Color Rules
+ Custom Reports
+ Annotations
+ Tags / Virtual Tags
+ Context
+
+ Recurrence
+ Shared library
+ purge command
+
+ True Color +
API + JSON
+ Import
+ Export
+ Hooks
+ Hooks v2
+ DOM
+ Helper commands
+
+ + on-sync hook
+ Full DOM
+ DOM access in rc
+ $ENV access in rc
+ Report columns as DOM refs
+
+ Attributes
+ User Defined Attributes (UDA) +
+ modified
+ priority as a UDA
+
+ template
+ rtype
+ Remove mask
+ Remove imask
+ Remove parent
+
+ org
+ group
+
Reports + Improved layouts
+ Improved Themes +
+ Daily, Weekly reports (history, ghistory)
+
+
Synchronization + task sync
+ task sync init (all tasks)
+
+ + task sync reset
+
TDB (task database) + Local file locking
+ Single file set
+ Single user +
+ + Threaded file load
+ Read-only mode +
I18N / L10N + UTF-8 support
+ deu-DEU
+ eng-USA
+ epo-RUS
+ esp-ESP
+ fra-FRA
+ ita-ITA
+ pol-POL
+ por-PRT
+
+ No I18N / L10N + + Migrate to gettext
+
Documentation + man: task
+ man: taskrc
+ man: task-color
+ man: task-sync
+ youtube: various
+ taskwarrior.org
+ taskwarrior.com: Support Site
+
+ + New video tutorials
+
Testing + C++ tests
+ Python tests
+ Sync tests
+ Parallel tests
+
+ Migration to Flod2
+
+
Tool Chain + GCC 4.7 / Clang 3.3
+ C++11 support
+ CMake
+
+ GCC 4.9 / Clang 3.4
+ Full C++11 support
+
+ Full C++14 support
+ Full C++17 support
+
+ + + + + + + + + + + + + + + + + + + + + + +
Tasksh
Technology/Feature
+ 1.1.0
+ Current

+ Released 2016-09-05 +
+ 1.2.0
+ Next

+ 2017 +
+ 1.x
+ Future +
Core + Review
+ libreadline
+ Shared library
+
+ + Pomodoro timer
+
Tool Chain + CMake
+ GCC 4.7 / Clang 3.3
+
+ GCC 4.9 / Clang 3.4
+ Full C++11 support
+
+ Full C++14 support
+ Full C++17 support
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Taskserver
Technology/Feature
+ 1.1.0
+ Current

+ Released 2015-05-10 +
+ 1.2.0
+ Next

+ 2017 +
+ 1.x
+ Future +
Core + Serial server + + Shared library
+
+ Threaded server +
Protocol + v1 + + v1.1 - client reset request
+
+ v1.2 +
DB (Data Storage) + + + GC +
Security + Validation + + + UUID:Cert Verification
+ Combined Certs +
Tool Chain + GCC 4.7 / Clang 3.3
+ CMake
+
+ GCC 4.9 / Clang 3.4
+ Full C++11 support
+
+ Full C++14 support
+ Full C++17 support
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Timewarrior
Technology/Feature
+ 1.0.0
+ Current

+ Released 2016-08-20 +
+ 1.1.0
+ Next

+ 2017 +
+ 1.x
+ Future +
Core + Shared library
+
+ + True Color +
Reports + summary report
+ gaps report
+ day chart
+ week chart
+ month chart
+ totals.py extension
+
+ +
Rules + Simple configuration rules + + + Rule System
+
Integration + Taskwarrior on-modify hook script + + +
Tool Chain + CMake
+ GCC 4.7 / Clang 3.3
+ C++11 support
+
+ GCC 4.9 / Clang 3.4
+ Full C++11 support
+
+ Full C++14 support
+ Full C++17 support
+
diff --git a/docs/rfcs/protocol.md b/docs/rfcs/protocol.md new file mode 100644 index 000000000..bd2f4e299 --- /dev/null +++ b/docs/rfcs/protocol.md @@ -0,0 +1,211 @@ +--- +title: "Taskwarrior - Sync Protocol" +--- + + +# Sync Protocol + + +## Introduction + +Taskwarrior data has typically been shared in several ways. Those include SCM +(source code management) systems, directory synchronizing software (such as +DropBox), and by use of the \'push\', \'pull\' and \'merge\' commands introduced +in version 1.9.3. + +While these methods work, they each have problems associated with the merging of +data. In the case of directory synchronizing software, there is no merging at +all - just simple file overwrite, despite many people believing that the data is +somehow combined and preserved. + +The Taskserver is a solution. It is an online/cloud storage and sync service for +taskwarrior data. It performs conflict-free data merging, and minimizes +bandwidth use. + +The Taskserver also provides multi-client access, so that a task added using a +web client could be immediately viewed using a mobile client, or modified using +taskwarrior. Choice of clients is important - people have widely varying +behaviors and tastes. + +The Taskserver also provides multi-user access, which introduces new +capabilities, such as list sharing and delegation. These will require later +modification to this protocol. + +The Taskserver protocol will be implemented by the taskd project and first used +in taskwarrior 2.3.0. Other clients will follow. + + +## Requirements + +In this document, we adopt the convention discussed in Section 1.3.2 of +[RFC1122](https://tools.ietf.org/html/rfc1122#page-16) of using the capitalized +words MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL to define the +significance of each particular requirement specified in this document. + +In brief: \"MUST\" (or \"REQUIRED\") means that the item is an absolute +requirement of the specification; \"SHOULD\" (or \"RECOMMENDED\") means there +may exist valid reasons for ignoring this item, but the full implications should +be understood before doing so; and \"MAY\" (or \"OPTIONAL\") means that this +item is optional, and may be omitted without careful consideration. + + +## Link Level + +The Taskserver protocol assumes a reliable data stream such as provided by TCP. +When TCP is used, a Taskserver listens on a single predetermined port *for the +given client* only. This means the server may be using multiple ports to serve +distinct sets of clients. + +This server is only an interface between programs and the task data. It does not +perform any user interaction or presentation-level functions. + + +## Transactions + +Each transaction is a single incoming message, with a single response message. +All communication therefore consists of a single \'send\', followed by a single +\'receive\', then termination. There are no sessions, and no continuously open +connections. The message format is described in the [Taskserver Message +Format](/docs/design/request) document. + + +## Responsibilities of the Server + +The server will maintain a set of transactions, in the original sequence, +punctuated by sync keys which are UUIDs. Each sync key represents a non- trivial +sync operation by a client. Each transaction is a [JSON-formatted +task](/docs/design/task), followed by a newline (\\n) character. The result +is a single file that contains interleaved lines of two types: tasks and sync +keys. + +This design allows the server to maintain a set of deltas such that multiple +clients may request a minimal set of changes since their last sync. + + +## Responsibilities of the Client + +This describes how Taskwarrior implements sync. + +All modifications to tasks (add, modify, done, delete \...) are recorded in the +form of a fully-composed [JSON-formatted task](/docs/design/task). The +formatted task is added to a local backlog.data file. If a task is modified a +second time, it is added again to the backlog.data file - the lines are not +combined. Each task SHALL have a \'modified\' date attribute that will help +resolve conflicts. + +On sync: + +- Send a \'sync\' type message with the entire contents of the backlog.data, + unmodified, as the message payload. +- Receive one of the following response codes: + - 201: This means \'no change\', and there is no further action to be + taken. + - 200: This means \'success\', and the message payload contains a set of + tasks and a sync key: + - The formatted tasks are to be stored as-is. These tasks will either + be appended to the client data or will overwrite existing client + data, based on the UUID of the task. No merge logic is necessary. + - The sync key will be written to the backlog.data file, overwriting + the previous contents, such that it will now contain only one line. + - 301: Redirect to : found in the \'info\' response header, will force the + client to resubmit the request to the new server. + - 3xx, 4xx, 5xx: The \'status\' field contains an error message. +- If the response contained any error or warning, the error should be shown to + the user. This provides an opportunity for the server to announce downtime, + or relocation. + +If no sync key is sent, the server cannot provide an incremental delta, and so +will send all task data, which should be stored as above. This should be the +case for a client making its first sync call. + +If an unrecognized attribute is present in the task data, the client MUST +preserve the attribute unmodified, and assume it is of type \'string\'. This +permits individual clients to augment the task data without other clients +stripping it meaningful data. This is how UDAs (user defined attributes) are +handled. + + +## Extensions + +This protocol was designed so that extensions to the protocol will take the form +of additional message types and status codes. + + +## Summary of Response Codes + +Status responses indicate the server\'s response to the last command received +from the client. The codes consist of a 3 digit numeric code. + +The first digit of the response broadly indicates the success, failure, or +progress of the previous command (based generally on +[RFC640](https://tools.ietf.org/html/rfc640) +[RFC821](https://tools.ietf.org/html/rfc821)): + + ----- ------------------------------------- + 1yz Positive Preliminary reply + 2yz Positive Completion reply + 3yz Positive Intermediate reply + 4yz Transient Negative Completion reply + 5yz Permanent Negative Completion reply + ----- ------------------------------------- + +The next digit in the code indicates the response category: + + ----- ------------------------------------------------- + x0z Syntax + x1z Information (e.g., help) + x2z Connections + x3z Authentication + x4z Unspecified as yet + x5z Taskd System (\...) + x8z Nonstandard (private implementation) extensions + ----- ------------------------------------------------- + +A summary of all status response are: + + ----- ----------- + 200 Success + 201 No change + ----- ----------- + + ----- ---------------------------------------------------------------------------------------------------------------------------------------------- + 300 Deprecated message type. This message will not be supported in future Taskserver releases. + 301 Redirect. Further requests should be made to the specified server/port. + 302 Retry. The client is requested to wait and retry the same request. The wait time is not specified, and further retry responses are possible. + ----- ---------------------------------------------------------------------------------------------------------------------------------------------- + + ----- ------------------------------------------ + 400 Malformed data + 401 Unsupported encoding + 420 Server temporarily unavailable + 421 Server shutting down at operator request + 430 Access denied + 431 Account suspended + 432 Account terminated + ----- ------------------------------------------ + + ----- ----------------------------------- + 500 Syntax error in request + 501 Syntax error, illegal parameters + 502 Not implemented + 503 Command parameter not implemented + 504 Request too big + ----- ----------------------------------- + + +## Security Considerations + +All communication with the Taskserver uses SSL 3.0 or TLS 1.0, 1.1 or 1.2. +Encryption is mandatory. Data is never transmitted in plain text. + + +## Limitations and Guidelines + +Some limitations exists to reduce bandwidth and load on the server. They are: + +- A client may only connect to a single server. Synchronization among a set of + servers is not supported. +- A client should attempt to minimize data bandwidth usage by maintaining a + local data store, and properly using sync keys. +- A client should minimize data transfers by limiting the frequency of sync + requests. diff --git a/docs/rfcs/recurrence.md b/docs/rfcs/recurrence.md new file mode 100644 index 000000000..641c87499 --- /dev/null +++ b/docs/rfcs/recurrence.md @@ -0,0 +1,203 @@ +--- +title: "Taskwarrior - Recurrence" +--- + +# Draft + +This is a draft design document. Your +[feedback](mailto:support@taskwarrior.org?Subject=Feedback) is welcomed. + +Recurrence +---------- + +Recurrence needs an overhaul to improve weaknesses and add new features. + +# Terminology + +- The hidden 'parent' task is called the template. +- Synthesis is the name for the generation of new recurring task instances + when necessary. +- The synthesized tasks are called instances. +- The index is the zero-based monotonically increasing number of the instance. +- Drift is the accumulated errors in time that cause a due date to slowly + change for each recurring task. + +# Criticism of Current Implementation + +- The `mask` attribute grows unbounded. +- Only strict recurrence cycles are supported. The example of mowing the lawn + is that you want to mow the lawn every seven days, but when you are four + days late mowing the lawn, the next mowing should be in seven days, not in + three. +- Intances generated on one machine and then synced, may collide with + equivalent unsynced instances tasks on another device, because the UUIDs are + different. +- You cannot `wait` a recurring task and have that wait period propagate to + all other child tasks. +- Task instances cannot individually expire. + +# Proposals + +## Proposal: Eliminate `mask`, `imaѕk` Attributes + +The `mask` attribute in the template is replaced by `last`, which indicates the +most recent instance index synthesized. Because instances are never synthesized +out of order, we only need to store the most recent index. The `imask` attribute +in the instance is no longer needed. + +## Proposal: Rename `parent` to `template` + +The name `parent` implies subtasks, and confuses those who inspect the +internals. The value remains the UUID of the template. This frees up the +namespace for future use with subtasks. + +## Proposal: New 'rtype' attribute + +To indicate the flavor of recurrence, support the following values: + +* `periodic` - Instances are created on a regular schedule. Example: send birthday flowers. It must occur on a regular schedule, and doesn't matter if you were late last year. This is the default value. +* `chained` - Instances are created back to back, so when one instance ends, the next begins, with the same recurrence. Example: mow the lawn. If you mow two days late, the next instance is not two days early to compensate. + +## Proposal: Use relative offsets + +The delta between `wait` and `due` date in the template should be reflected in +the delta between `wait` and `due` date in the instance. Similarly, +'scheduled' must be handled the same way. + +## Proposal: On load, auto-upgrade legacy tasks + +Upgrade template: + +- Add `rtype:periodic` +- Add `last:N` where `N` is the length of `mask` +- Delete `mask` + +Upgrade instance: + +- Rename `parent` to `template` +- Delete `imask` +- Update `wait` if not set to: `wait:due + (template.due - template.wait)` +- Update `scheduled` if not set to: + `scheduled:due + (template.due - template.scheduled)` + +## Proposal: Deleting a chained instance + +Deleting a `rtype:chained` instance causes the next chained instance to be +synthesized. This gives the illusion that the due date is simply pushed out to +`(now + template.recur)`. + +## Proposal: Modification Propagation + +TBD + +## Proposal: Exotic Dates + +Expand date specifications to use pattern phrases: + +- `4th thursday in November` +- `4th thursday of November` +- `Friday before easter` +- `next Tuesday` +- `last Tuesday` +- `last July` +- `weekend` +- `3 days before eom` +- `in the morning` +- `4pm` +- `noon` +- `midnight` + +Got suggestions? + +## Proposal: User-Defined Week Start + +TBD + +# Implementation + +## Implementation: Adding a new `periodic` template + +When adding a new periodic template: + + task add ... due:D recur:R wait:D-1wk scheduled:D-1wk until:U + +Creates: + + template.uuid: NEW_UUID + template.description: ... + template.entry: now + template.modified: now + template.due: D + template.recur: R (stored in raw form, ie 'P14D') + template.wait: D-1wk + template.scheduled: D-1wk + template.until: U + template.rtype: periodic + template.last: + +Creating the Nth instance (index N): + + Clone instance from template. + + instance.uuid: NEW_UUID + instance.modified: now + instance.due: template.due + (N * template.recur) + instance.wait: instance.due + (template.due - template.wait) + instance.scheduled: instance.due + (template.due - template.scheduled) + instance.start: + + template.last: N + +## Implementation: Adding a new `chained` template + +When adding a new chained template: + + task add ... due:D recur:R wait:D-1wk scheduled:D-1wk until:U rtype:chained + +Creates: + + template.uuid: NEW_UUID + template.description: ... + template.entry: now + template.modified: now + template.due: D + template.recur: R (stored in raw form, ie 'P14D') + template.wait: D-1wk + template.scheduled: D-1wk + template.until: U + template.rtype: chained + +Creating the Nth instance (index N): + + Clone instance from template. + + instance.uui d: NEW_UUID + instance.mod ified: now + instance.due : instance[N-1].end + template.recur + instance.wai t: instance.due + (template.due - template.wait) + instance.sch eduled: instance.due + (template.due - template.scheduled) + instance.sta rt: + +Chained tasks do not obey `rc.recurrence.limit`, and show only one pending task +at a time. + +## Implementation: Special handling for months + +Certain recurrence periods are inexact: + +- P1M +- P1Y +- P1D + +When the recurrence period is `P1M` the number of days in a month varies and +causes drift. + +When the recurrence period is `P1Y` the number of days in a year varies and +causes drift. + +When the recurrence period is `P1D` the number of hours in a day varies due to +daylight savings, and causes drift. + +Drift should be avoided by carefully implementing: + + instance.due: template.due + (N * template.recur) diff --git a/docs/rfcs/request.md b/docs/rfcs/request.md new file mode 100644 index 000000000..293b807d6 --- /dev/null +++ b/docs/rfcs/request.md @@ -0,0 +1,221 @@ +--- +title: "Taskwarrior - Request" +--- + + +# Taskserver Message Format + +The Taskserver accepts and emits only messages. These messages look somewhat +like email, as defined in [RFC821](https://tools.ietf.org/html/rfc821), +[RFC2822](https://tools.ietf.org/html/rfc2822). + +The message format allows for data, metadata, and extensibility. This +combination allows the Taskserver to accommodate current and future needs. This +document describes the message format, and the supported message types. + + +## Requirements + +In this document, we adopt the convention discussed in Section 1.3.2 of +[RFC1122](https://tools.ietf.org/html/rfc1122#page-16) of using the capitalized +words MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL to define the +significance of each particular requirement specified in this document. + +In brief: \"MUST\" (or \"REQUIRED\") means that the item is an absolute +requirement of the specification; \"SHOULD\" (or \"RECOMMENDED\") means there +may exist valid reasons for ignoring this item, but the full implications should +be understood before doing so; and \"MAY\" (or \"OPTIONAL\") means that this +item is optional, and may be omitted without careful consideration. + + +## Encoding + +All messages are UTF8-encoded text. + + +## Message Format + +This format is based on [RFC2822](https://tools.ietf.org/html/rfc2822), +\'Internet Message Format\'. Here is an example of the format: + + + name: value + name2: value2 + + payload + +There are three sections. The first is the size, which is a 4-byte, big- Endian, +binary byte count of the length of the message, including the 4 bytes for the +size. + +The header section is a set of name/value pairs separated by newline characters +(U+000D). The name is separated from the value by \': \' (colon U+003A, space +U+0020) The header section is terminated by two consecutive newline (U+000D) +characters. All text is UTF8-encoded. + +The payload section is arbitrary, and message type-specific. However, it is +still UTF8-encoded text. + + +## Message Requirements + +Messages SHALL contain particular headers. Those are: + +- type +- protocol +- client + +The \'type\' value is what determines the interpretation of the payload. + +The \'protocol\' value should be \'v1\', or any subsequently published protocol +version. + +The \'client\' represent the client identifier, so that any special cases can be +handled. For example, an emergency fix that is client version-specific could be +released, to support users that have not updated their client, or perhaps the +client has not released a fix. The form of the \'version\' value is: + + + +As an example: + + taskwarrior 2.3.0 + +DO NOT spoof any other software using this client value. If another client is +spoofed, then patches addressing protocol errors may break working software. + + +## Auth Data + +Every request from the client SHALL contain \"auth\" information, which involves +these header entries: + + org: + user: + key: + +The user and org fields uniquely identify a user. + +The key field is generated when a new server account is set up. It is a shared +secret, equivalent to a password, and should be protected. + +Authentication failure can result in these errors: + +- 430 Authentication failed +- 431 Account suspended + + +## Status Data + +Every response from the Taskserver SHALL contain status data: + + code: + status: + +The code is a numeric status indicator defined in the [Sync +Protocol](/docs/design/protocol). + + +## Payload Data + +Payload data is optional, arbitrary and message type dependent. It is always +UTF8-encoded text. + + +## Message Types + +The Taskserver supports several message types, thus providing a set of +primitives for use by clients. + +It is expected that the number of supported ticket types will increase over +time. + + +## Sync Message + +The \"sync\" message always originates from the client, but the response will +contain data from the server. A sync is therefore a single request with a single +response. + +The \"sync\" message type MUST contain the following headers: + +- type +- org +- user +- key +- client +- protocol + +The \"sync\" message payload has this format: + + + + + ... + + +Here is an example of a sync message: + + type: sync + org: + user: + key: + client: task 2.3.0 + protocol: v1 + + 2e4685f8-34bc-4f9b-b7ed-399388e182e1 + {"description":"Test data","entry":"20130602T002341Z","status":"pending"} + +The request contains the proper auth section, and the body contains the current +sync key followed by a newline characters (U+000D), then a list of +JSON-formatted tasks \[2\] each separated by a newline character (U+000D). + +An example response message might be: + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 200 + status: Ok + + 45da7110-1bcc-4318-d33e-12267a774e0f + +The status indicates success, and the payload contains zero remote task +modifications, followed by a sync key. + + +## Statistics Message + +The message format іs simply: + + type: statistics + org: + user: + key: + client: taskd 1.0.0 + protocol: v1 + +There is no payload. An example response message might be: + + type: response + client: taskd 1.0.0 + protocol: v1 + code: 200 + status: Ok + average request bytes: 0 + average response bytes: 0 + average response time: 0.000000 + errors: 0 + idle: 1.000000 + maximum response time: 0.000000 + total bytes in: 0 + total bytes out: 0 + tps: 0.000000 + transactions: 1 + uptime: 28 + +There is no payload, and the results are in the header variables. + +Note that the statistics gathered by the server are growing, which means new +values are occasionally added to the response message. Existing values will not +be removed. diff --git a/docs/rfcs/rules.md b/docs/rfcs/rules.md new file mode 100644 index 000000000..cf08acd3a --- /dev/null +++ b/docs/rfcs/rules.md @@ -0,0 +1,231 @@ +--- +title: "Taskwarrior - Rule System" +--- + +## Work in Progress + +This design document is a work in progress, and subject to change. Once +finalized, the feature will be scheduled for an upcoming release. + + +# Rule System + +The rule system is a framework that supports highly configurable features, with +runtime evaluation, DOM access and an internal API. Implementing a rule system +meets the goal of shrinking and stabilizing the product core, while adding new +features, and enabling many more. + + +## Required Enhancements + +To prepare for a Rules System, various subsystems must first be enhanced: + +- DOM references need to be unambiguous, and will all have the `dom.` prefix. + +- DOM references need to be able to access any Taskwarrior data, in any + +- Custom reports will change from referencing `[.]` to simply + `` + +- RC file syntax needs to be enhanced, so support rule definitions, which are + multi-line blocks that are indentation-sensitive + +- RC file syntax will support two ways of specifying the same data: + + a.b.c=... + + a: + b: + c=... + +- RC file syntax will allow the use of environment variables inline: + + name=${TERM} + include ${HOME}/.taskrc_local + +- The `Variant` object will migrate to `libshared` + +- The expression evaluator `Eval` object will migrate to `libshared` + +- The column objects will gain a more structured base class, and will serve as + providers for DOM references + +- The \'exec\' command will be able to run a rule, if the reference is correct + +- Taskwarrior will store state data in a new `state.data` file + +- `Config` object needs to use the `rat` parser, to tackle the more complex + syntax + +- The RC file will support environment variable expansion, where `${NAME}` + will be replaced by its corresponding value at launch time + +At that point, the rules system can be implemented in `libshared`, and will use +a pluggable architecture to allow its integration into several projects. + + +## DOM Enhancements + +DOM references will be enhanced, with many more references supported. All DOM +references will begin with `dom.`, yielding unambiguous references. References +will have a type. Types will support sub-references (`.`, +`.`, `.`), and display formats included. + + dom . [ .] [. ] . + + dom . 123 . entry . year . yyyy + dom . 123 . entry + dom . 123 . tags + dom . 123 . tags . count + dom . 123 . tags . 1 + +In addition to direct attribute access, DOM references will also support tw +references beyond the current set: + + dom.rc. + dom.cli.args + dom.terminal.width + dom.terminal.height + dom.system.version + dom.system.oѕ + +And will also support higher-level constructs that do not directly correlate to +attributes, for example: + + dom.active Boolean indicator of any active tasks + dom.synced Boolean indicator of the need to sync + dom.rc.path String path of .taskrc file (or override) + dom.data.path String path of data directory + dom.hooks.path String path of hooks directory + +Finally, access to state: + + dom.state.program + dom.state.sync.last + dom.state.sync.configured + dom.state.run.last + dom.state.context + + +## RC Syntax Changes + +The current configuration system supports only two different forms of syntax: + + = [ ] + + include + +A rule is a new form of syntax that consists of the rule keyword, a name, +optional trigger, followed by indented actions in the form of API calls and flow +control. For example: + + rule myRule() on_launch: + # Some code here + +A rule definition will appear in the RC file, alongside all the existing +settings. The rule syntax will require a blank line to terminate the rule +definition, the result being that the RC file should be quite readable, although +it will look like Python. + + +## Hook Scripts + +While this functionality can also be implemented using hook scripts, rules will +run in-process, and therefore do not require external interpreters to be +launched every time. This creates the potential to run faster than a hook +script. + +For complex processing, hook scripts will be the preferred mechanism, but as the +rules system matures, rules will be made to run more quickly. With adequate +performance, a rule will be the preferred implementation over a hook script. +This is not expected to be the case at first. + +Hook scripts are not likely to be extended beyond their current form, and with +greater DOM access and a growing API, rules should be able to supplant most hook +script use cases. + + +## Rule Triggers + +The set of supported rule types will include: + +* `on_launch` - Triggered on program launch. +* `on_add` - Triggered when a task is added. A context task will be provided. The rule can modify the task, and approve or reject it. +* `on_modify` - Triggered when a task is modified. A before and after context task will be provided. The rule can modify the task, and approve or reject it. +* `on_exit` - Triggered on program exit. +* `color` - Triggered when colors are being determined. +* `virtual tag` - Defines a new virtual tag. +* `format` - Triggered when an attribute needs formatting, defines are new format. + +More rules types will be added for more capabilities in future releases. + + +## API + +The API is a simple set of actions that may be taken by a rule. + +* `debug()` - Displays the string in debug mode only and continues processing. +* `warn()` - Displays the string as a warning continues processing. +* `error()` - Displays the string as an error and terminates processing. +* `exec( [ ... ])` - Executes the external program and passes arguments to it. If the program exits with non-zero status, it is treated as an error. +* `return ` - Provides a result value for the rule, when necessary. + +This is a very limited set at first, and more API calls will be added to support +capabilities in future releases. + + +## Grammar + +The grammar closely tracks that of Python. Blocks are indented consistently. + +* `if : ... else: ... ` - The condition is a full Algebraic expression, and supports none of the command line conveniences. Terms must be combined with logical operators. The condition is an expression that is evaluated and converted to a Boolean value. +* `for in : ` - There is no native type for a collection, but there are DOM references (`tags` \...) that reference collections. This provides a way to iterate. +* `set = ` - Writes to a named type. The name may be a writable DOM object (`dom...`) or temporary variable storage (`tmp...`). Writing to a read-only DOM reference is an error. +* `([]) ` - A function is either a rule or an API call. Calling an undefined function is an error. + + +## Examples + +Here are some example rules which illustrate the syntax and API. + +The replacement for the nag feature: + + rule Nag(before, after) on-modify: + if before.urgency < tasks.max.urgency: + warn ‘You have more urgent tasks’ + + if after.status == 'completed' and before.urgency < (dom.urgency.max - 2.0): + warn 'You have more urgent tasks!' + +Correct commonly misspelled word: + + rule CorrectSpelling(task) on_add: + set task.description = substitute(task.description, 'teh', 'the') + +Abbreviation expansion: + + rule ExpandAbbreviation(task) on_modify: + set task.description = substitute(task.description, '/TW-\d+/', 'https:\/\/github.com\/GothenburgBitFactory\/taskwarrior\/issues\/\1') + +Warn on missing project: + + rule WarnOnMissingProject(task) on_add: + if task.project == ‘’: + warn(‘Project not specified’) + +Color rule: + + rule ColorizeDue(task) color: + if task.due > now: + if task.due < (now + 5d): + return dom.rc.color.due + else: + return dom.rc.color.due.later + +Policy: + + rule policyProject(task) on_add: + if task.project == '': + if rc.default.project == '': + error('You must specify a project') + set task.project = rc.default.project diff --git a/docs/rfcs/sync.md b/docs/rfcs/sync.md new file mode 100644 index 000000000..df5424ab2 --- /dev/null +++ b/docs/rfcs/sync.md @@ -0,0 +1,253 @@ +--- +title: "Taskwarrior - Taskserver Sync Algorithm" +--- + + +# Taskserver Sync Algorithm + +This document describes how task changes are merged by the Taskserver. It does +not describe [the protocol](/docs/design/protocol) used by the Taskserver. + +The Taskserver merges tasks from multiple sources, resulting in conflict- free +syncing of data. The algorithm used to achieve this is simple and effective, +paralleling what SCM systems do to perform a rebase. + + +## Requirements + +In this document, we adopt the convention discussed in Section 1.3.2 of +[RFC1122](https://tools.ietf.org/html/rfc1122#page-16) of using the capitalized +words MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL to define the +significance of each particular requirement specified in this document. + +In brief: \"MUST\" (or \"REQUIRED\") means that the item is an absolute +requirement of the specification; \"SHOULD\" (or \"RECOMMENDED\") means there +may exist valid reasons for ignoring this item, but the full implications should +be understood before doing so; and \"MAY\" (or \"OPTIONAL\") means that this +item is optional, and may be omitted without careful consideration. + + +## Problem Definition + +The sync algorithm considers a single task, with multiple changes occurring in +two separate locations that must be resolved. The two locations are the local +machine and the server. This results in two parallel change sequences. + +Examples using multiple clients collapse down to the simple two-branch case +because the clients are merged serially. + + +## Change Sequence + +A sequence of changes to the same task is represented as: + + T0 --> T1 --> T2 + +Although all examples are of the two-branch variety, some involve trivial +branches. Going through these examples will illustrate the algorithm. First the +legend: + + T0 Represents the original task, the base. + T1 Represents the task with a non-trivial set of changes. + T2 Represents the task with further changes. + + +## Deltas + +The transition from T0 \--\> T1 can be seen as a transform applied to T0, +resulting in T1. That transform is the delta (d1) between T0 and T1, which is a +subtractive term: + + d1 = (T1 - T0) + +Therefore: + + T0 --> T1 = T0 + d1 + = T0 + (T1 - T0) + +This states that the transition from T0 to T1 is the application of a delta to +the original, T0, which results in T1. Applying this to the whole change +sequence yields: + + T0 --> T1 --> T2 = T0 + d1 + d2 + = T0 + (T1 - T0) + (T2 - T1) + + +## Use Case Classification + +Because clients sync requests are processed serially, there is no need to +consider the multiple client cases. This means there is only ever the case with +two parallel change sequences = the two branch case. + + +## Two Branch Case + +The two branch case represents changes made to the same task in two locations, +resulting in two deltas that must be applied to the same base. + + T0 --> T1 + T0 --> T2 + +This reduces to a base with two deltas, but the order in which the deltas are +applied is important. For example: + + T0 + d1 + d2 =/= T0 + d2 + d1 + +The application of deltas is not commutative, except in the trivial case where +the two deltas are identical, or the deltas do not overlap. The deltas therefore +need to be applied in the correct sequence. Tasks have metadata that indicates +the last modified time, which dictates the sequence. Assuming d1 occurred before +d2, this neatly collapses down to a single branch sequence: + + T0 + d1 + d2 = T3 + +Note that the result in this case is T3, because it will be neither T1 nor T2, +unless the deltas are identical. + + +## Two Branch, Multiple Changes Case + +The two branch case can be complicated by multiple changes per branch: + + T0 --> T1 --> T3 --> T5 + T0 --> T2 --> T4 + +Note that the numbers were chosen to represent the order in which the changes +were made. First a list of deltas is generated: + + T0 --> T1 = d1 + T1 --> T3 = d3 + T3 --> T5 = d5 + T0 --> T2 = d2 + T0 --> T4 = d4 + + d1, d3, d5, d2, d4 + +Then the deltas are sorted by modified time: + + d1, d2, d3, d4, d5 + +Then epplied to the base, yielding T6: + + T0 + d1 + d2 + d3 + d4 +d5 = T6 + + +## Two Branch Case Example + +Suppose the base task looks like this: + + T0 project:ONE due:tomorrow priority:H +tag1 Original description + +The first branch looks like this: + + T1 project:TWO due:23rd priority:H +tag1 Original description + +The second branch looks like this: + + T2 project:ONE due:tomorrow priority:H +tag1 Modified description + +Delta d1 is: + + T0 project:ONE due:tomorrow priority:H +tag1 Original description + T1 project:TWO due:23rd priority:H +tag1 Original description + ---------------------------------------------------------------------- + d1 project:TWO due:23rd + +Delta d2 is: + + T0 project:ONE due:tomorrow priority:H +tag1 Original description + T2 project:ONE due:tomorrow priority:H +tag1 Modified description + ---------------------------------------------------------------------- + d2 Modified description + +If d1 occurred before d2, the result is: + + T3 = T0 + d1 + d2 + = T0 + (project:TWO due:23rd) + (Modified description) + + T3 = project:TWO due:23rd priority:H +tag1 Modified description + + +## Use Cases + +A range of illustrated use cases, from the trivial to the complex will show the +algorithm in use. + + +## Use Case 1: New Local Task + +Initial state: + + Server: - + Client: T0 + +The server has no data, and so T0 is stored. The result is now: + + Server: T0 + Client: T0 + + +## Use Case 2: Local Change + +Initial state: + + Server: T0 + Client: T0 --> T1 + +The server resolves the change: + + T0 --> T1 = T0 + d1 + = T1 + +T1 is stored. The result is now: + + Server: T0 --> T1 + Client: T1 + + +## Use Case 3: Local and Remote Change + +Initial state: + + Server: T0 --> T1 + Client: T0 --> T2 + +This is the two branch case, and the deltas are generated: + + T0 --> T1 = T0 + d1 + T0 --> T2 = T0 + d2 + +The order of change is determine to be d1, d2, yielding T3: + + T3 = T0 + d1 + d2 + +T3 is stored on the server, and returned to the client. The result is now: + + Server: T0 --> T1 --> T2 --> T3 + Client: T3 + + +## Use Case 4: Multiple Local and Remote Changes + +Initial state: + + Server: T0 --> T1 --> T3 + Client: T0 --> T2 --> T4 + +This is the two branch case, and the deltas are generated: + + T0 --> T1 = T0 + d1 + T1 --> T3 = T0 + d3 + T0 --> T2 = T0 + d2 + T2 --> T4 = T0 + d4 + + d1, d3, d2, d4 + +The order of change is determine to be d1, d2, d3, d4, yielding T5: + + T5 = T0 + d1 + d2 + d3 + d4 + +T5 is stored on the server, and returned to the client. The result is now: + + Server: T0 --> T1 --> T2 --> T3 --> T4 --> T5 + Client: T5 diff --git a/docs/rfcs/task.md b/docs/rfcs/task.md new file mode 100644 index 000000000..ee03738fd --- /dev/null +++ b/docs/rfcs/task.md @@ -0,0 +1,761 @@ +--- +title: "Taskwarrior - Taskwarrior JSON Format" +--- + + +# Taskwarrior JSON Format + +When Taskwarrior exchanges data, it uses [JSON](https://www.json.org/). This +document describes the structure and semantics for tasks exported from +Taskwarrior, imported to Taskwarrior, or synced with the Taskserver. + +Any client of the Taskserver will need to communicate task information. This +document describes the format of a single task. It does not describe the +communication and sync protocol between client and server. + +This document is subject to change. The data attributes are also subject to +change. + + +## Requirements + +In this document, we adopt the convention discussed in Section 1.3.2 of +[RFC1122](https://tools.ietf.org/html/rfc1122#page-16) of using the capitalized +words MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL to define the +significance of each particular requirement specified in this document. + +In brief: \"MUST\" (or \"REQUIRED\") means that the item is an absolute +requirement of the specification; \"SHOULD\" (or \"RECOMMENDED\") means there +may exist valid reasons for ignoring this item, but the full implications should +be understood before doing so; and \"MAY\" (or \"OPTIONAL\") means that this +item is optional, and may be omitted without careful consideration. + + +## General Format + +The format is JSON, specifically a JSON object as a single line of text, +terminated by a newline (U+000D). + +The JSON looks like this: + + {"description":"One two three","status":"pending", ... } + +While this is not a valid task (there are missing fields), the format is +illustrated. + +All attribute names are quoted with \" (U+0022). A name will always have a +corresponding value, and if a value is blank, then the name/value pair is +omitted from the line. Newline characters are not permitted within the value, +meaning that a task consists of a single line of text. + +All data is UTF8. + + +## Data Types + +There are five data types used in the task format. + + +## Data Type: String + +Strings may consist of any UTF8 encoded characters. + + +## Data Type: Fixed String + +A fixed string is one value from a set of acceptable values, such as a priority +level, where the values may only be \"\", \"L\", \"M\" or \"H\". + + +## Data Type: UUID + +A UUID is a 32-hex-character lower case string, formatted in this way: + + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +An example: + + 296d835e-8f85-4224-8f36-c612cad1b9f8 + + +## Data Type: Integer + +Integers are rendered in a simple fashion: + + 123 + + +## Data Type: Date + +Dates are rendered in ISO 8601 combined date and time in UTC format using the +template: + + YYYYMMDDTHHMMSSZ + +An example: + + 20120110T231200Z + +No other formats are supported. + + +## Data Type: Duration + +Duration values represent a time period. They take the form: + + [[] ] + +Some examples include: + +- -3days +- annual +- 4hrs + +The supported units are: + +- annual +- biannual +- bimonthly +- biweekly +- biyearly +- daily +- days +- day +- d +- fortnight +- hours +- hour +- hrs +- hr +- h +- minutes +- mins +- min +- monthly +- months +- month +- mnths +- mths +- mth +- mos +- mo +- quarterly +- quarters +- qrtrs +- qtrs +- qtr +- q +- seconds +- secs +- sec +- s +- semiannual +- sennight +- weekdays +- weekly +- weeks +- week +- wks +- wk +- w +- yearly +- years +- year +- yrs +- yr +- y + +Note that some values lack precision, for example \"2q\" means two quarters, or +half a year. + +Note that not all combinations of and make sense, for example \"3annual\" makes +no sense, but evaluates to \"3years\". + + +## The Attributes + +Here are the standard attributes that may comprise a task: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType
statusString
uuidUUID
entryDate
descriptionString
startDate
endDate
dueDate
untilDate
waitDate
modifiedDate
scheduledDate
recurString
maskString
imaskInteger
parentUUID
projectString
priorityString
dependsString
tags *String
annotation *String
(UDA)?
+ +\* Both tags and annotations are lists of strings and objects. + +Any UDA fields are assumed to be of type string. + +There are other forms, which are conditional upon the state of a task: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Status ValuePendingDeletedCompletedWaitingRecurring ParentRecurring Child
statusReqdReqdReqdReqdReqdReqd
uuidReqdReqdReqdReqdReqdReqd
entryReqdReqdReqdReqdReqdReqd
descriptionReqdReqdReqdReqdReqdReqd
startOptOptOptOptOptOpt
endReqdReqd
dueOptOptOptOptReqdOpt
untilOptOptOptOptOptOpt
scheduledOptOptOptOptOptOpt
waitReqd
recurReqdReqd
maskIntrn
imaskIntrn
parentReqd
annotationOptOptOptOptOptOpt
projectOptOptOptOptOptOpt
tagsOptOptOptOptOptOpt
priorityOptOptOptOptOptOpt
dependsOptOptOptOptOptOpt
modifiedIntrnIntrnIntrnIntrnIntrnIntrn
UDAOptOptOptOptOptOpt
+ +(Legend: Reqd = required, Opt = optional, Intrn = Internally generated) + +All tasks have four required fields. There are other states in which a task may +exist, and the requirements change. At a minimum, a valid task contains: + +- uuid +- status +- entry +- description + +*Deleted*\ +A deleted task MUST also have \"status\":\"deleted\", an \"end\" date and a +\"modified\" date. + +*Completed*\ +A completed task MUST also have \"status\":\"completed\", an \"end\" date and a +\"modified\" date. + +*Waiting*\ +A waiting task MUST also have \"status\":\"waiting\" and a \"wait\" date. The +task is hidden from the user, until that \"wait\" date has passed, whereupon the +status reverts to \"pending\", and the \"wait\" date is removed. + +*Recurring Parent*\ +When a recurring task is entered, it MUST have \"status\":\"recurring\", a +\"recur\" period and a \"due\" date. It MAY also have an \"until\" date. +Recurring parent tasks are hidden from the user. + +*Recurring Child*\ +A recurring child task is not created by the user, but is cloned from the +recurring parent task by the Taskserver. It may be modified by the user. On +completion, there is special handling to be done. See section 3.11. + + +## Additional Attributes + +There MAY be other fields than those listed above in a task definition. Such +fields MUST be preserved intact by any client, which means that if a task is +downloaded that contains an unrecognized field, that field MUST not be modified, +and MUST continue to exist in the task.. + +User Defined Attributes (UDAs) are additional fields. + + +## Attribute Details + +The individual fields convey important information about a task, and in some +cases work only in collusion with other fields. All such details are listed +here. + + +## Attribute: status + +The status field describes the state of the task, which may ONLY be one of these +literal strings: + + "status":"pending" + "status":"deleted" + "status":"completed" + "status":"waiting" + "status":"recurring" + +A pending task is a task that has not yet been completed or deleted. This is the +typical state for a task. + +A deleted task is one that has been removed from the pending state, and MUST +have an \"end\" field specified. Given the required \"entry\" and \"end\" field, +it can be determined how long the task was pending. + +A completed task is one that has been removed from the pending state by +completion, and MUST have an \"end\" field specified. Given the required +\"entry\" and \"end\" fields, it can be determine how long the task was pending. + +A waiting task is ostensibly a pending task that has been hidden from typical +view, and MUST have a \"wait\" field containing the date when the task is +automatically returned to the pending state. If a client sees a task that is in +the waiting state, and the \"wait\" field is earlier than the current date and +time, the client MUST remove the \"wait\" field and set the \"status\" field to +\"pending\". + +A recurring task is essentially a parent template task from which child tasks +are cloned. The parent remains hidden from view, and contains a \"mask\" field +that represents the recurrences. Each cloned child task has an \"imask\" field +that indexes into the parent \"mask\" field, as well as a \"parent\" field that +lists the UUID of the parent. + + +## Attribute: uuid + +When a task is created, it MUST be assigned a new UUID by the client. Once +assigned, a UUID field MUST NOT be modified. UUID fields are permanent. + + +## Attribute: entry + +When a task is created, it MUST be assigned an \"entry\" date by the client. +This is the creation date of the task. + + +## Attribute: description + +When a task is created, it MUST have a \"description\" field value, which +contains UTF8 characters. A \"description\" field may not contain newline +characters, but may contain other characters, properly escaped. See + for details. + + +## Attribute: start + +To indicate that a task is being worked on, it MAY be assigned a \"start\" +field. Such a task is then considered Active. + + +## Attribute: end + +When a task is deleted or completed, is MUST be assigned an \"end\" field. It is +not valid for a task to have an \"end\" field unless the status is also +\"completed\" or \"deleted\". If a completed task is restored to the \"pending\" +state, the \"end\" field is removed. + + +## Attribute: due + +A task MAY have a \"due\" field, which indicates when the task should be +completed. + + +## Attribute: until + +A recurring task MAY have an \"until\" field, which is the date after which no +more recurring tasks should be generated. At that time, the parent recurring +task is set to \"completed\". + + +## Attribute: wait + +A task MAY have a \"wait\" field date, in conjunction with a \"status\" of +\"waiting\". A waiting task is one that is not typically shown on reports until +it is past the wait date. + +An example of this is a birthday reminder. A task may be entered for a birthday +reminder in 10 months time, but can have a \"wait\" date 9 months from now, +which means the task remains hidden until 1 month before the due date. This +prevents long-term tasks from cluttering reports until they become relevant. + + +## Attribute: recur + +The \"recur\" field is for recurring tasks, and specifies the period between +child tasks, in the form of a duration value. The value is kept in the raw state +(such as \"3wks\") as a string, so that it may be evaluated each time it is +needed. + + +## Attribute: mask + +A parent recurring task has a \"mask\" field that is an array of child status +indicators. Suppose a task is created that is due every week for a month. The +\"mask\" field will look like: + + "----" + +This mask has four slots, indicating that there are four child tasks, and each +slot indicates, in this case, that the child tasks are pending (\"-\"). The +possible slot indicators are: + +* `-` - Pending +* `+` - Completed +* `X` - Deleted +* `W` - Waiting + +Suppose the first three tasks has been completed, the mask would look like this: + + "+++-" + +If there were only three indicators in the mask: + + "+-+" + +This would indicate that the second task is pending, the first and third are +complete, and the fourth has not yet been generated. + + +## Attribute: imask + +Child recurring tasks have an \"imask\" field instead of a \"mask\" field like +their parent. The \"imask\" field is a zero-based integer offset into the +\"mask\" field of the parent. + +If a child task is completed, one of the changes that MUST occur is to look up +the parent task, and using \"imask\" set the \"mask\" of the parent to the +correct indicator. This prevents recurring tasks from being generated twice. + + +## Attribute: parent + +A recurring task instance MUST have a \"parent\" field, which is the UUID of the +task that has \"status\" of \"recurring\". This linkage between tasks, +established using \"parent\", \"mask\" and \"imask\" is used to track the need +to generate more recurring tasks. + + +## Attribute: annotation\_\... + +Annotations are strings with timestamps. Each annotation itself has an \"entry\" +field and a \"description\" field, similar to the task itself. Annotations form +an array named \"annotations\". For example (lines broken for clarity): + + "annotations":[ + {"entry":"20120110T234212Z","description":"Remember to get the mail"}, + {"entry":"20120110T234559Z","description":"Pay the bills"} + ] + + +## Attribute: project + +A project is a single string. For example: + + "project":"Personal Taxes" + +Note that projects receive special handling, so that when a \".\" (U+002E) is +used, it implies a hierarchy, which means the following two projects: + + "Home.Kitchen" + "Home.Garden" + +are both considered part of the \"Home\" project. + + +## Attribute: tags + +The \"tags\" field is an array of string, where each string is a single word +containing no spaces. For example: + + "tags":["home","garden"] + + +## Attribute: priority + +The \"priority\" field, if present, MAY contain one of the following strings: + + "priority":"H" + "priority":"M" + "priority":"L" + +These represent High, Medium and Low priorities. An absent priority field +indicates no priority. + + +## Attribute: depends + +The \"depends\" field is a string containing a comma-separated unique set of +UUIDs. If task 2 depends on task 1, then it is task 1 that must be completed +first. Task 1 is considered a \"blocking\" tasks, and task 2 is considered a +\"blocked\" task. For example: + + "depends":",, ..." + +Note that in a future version of this specification, this will be changed to a +JSON array of strings, like the \"tags\" field. + + +## Attribute: modified + +A task MUST have a \"modified\" field set if it is modified. This field is of +type \"date\", and is used as a reference when merging tasks. + + +## Attribute: scheduled + +A task MAY have a \"scheduled\" field, which indicates when the task should be +available to start. A task that has passed its \"scheduled\" data is said to be +\"ready\". + + +## User Defined Attributes + +A User Defined Attribute (UDA) is a field that is defined via configuration. +Given that the configuration is not present in the JSON format of a task, any +fields that are not recognized are to be treated as UDAs. This means that if a +task contains a UDA, unless the meaning of it is understood, it MUST be +preserved. + +UDAs may have one of four types: string, numeric, date and duration. + diff --git a/docs/rfcs/week.png b/docs/rfcs/week.png new file mode 100644 index 0000000000000000000000000000000000000000..d6b32044381dcd119a88ff3a470e48e8baecc207 GIT binary patch literal 11318 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 BGME4W literal 0 HcmV?d00001 diff --git a/docs/rfcs/workweek.md b/docs/rfcs/workweek.md new file mode 100644 index 000000000..1de1ed36e --- /dev/null +++ b/docs/rfcs/workweek.md @@ -0,0 +1,35 @@ +--- +title: "Taskwarrior - Work Week Support" +--- + +## Work in Progress + +This design document is a work in progress, and subject to change. Once +finalized, the feature will be scheduled for an upcoming release. + + +# Work Week Support + +Taskwarrior supports the idea that a week starts on either a Sunday or a Monday, +as determined by configuration. This was added eight years ago, simply for +display purposes in the `calendar` report. Since then its use has propagated and +it influences the `sow` date reference.0 + +Further requests have been made to make this more flexible, so that the notion +of \'weekend\' can be defined. Furthermore, the idea that every week has a +weekend has also been questioned. + +It has become clear that a `weekstart` setting, and the notion of a weekend are +no longer useful. + + +## Proposed Support + +One option is to allow the user to completely define a work week in the +following way: + + workweek=1,2,3,4,5 + +With Sunday as day zero, this states that the work week is the typical Monday - +Friday. From this setting, the meaning of `soww` and `eoww` can be determined, +as well as `recur:weekday`. diff --git a/docs/rfcs/year.png b/docs/rfcs/year.png new file mode 100644 index 0000000000000000000000000000000000000000..a8e54d6497a5c8fcd5c966adabea7401e1a25716 GIT binary patch literal 15455 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