init
This commit is contained in:
commit
76266cedc6
23 changed files with 4041 additions and 0 deletions
308
src/README.rst
Normal file
308
src/README.rst
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
############
|
||||
Introduction
|
||||
############
|
||||
|
||||
This document defines the canonical format, semantics, and processing rules
|
||||
for the repository-root ``TODO`` file. The file is a human-friendly,
|
||||
append-only (allowing modifications) issue tracker that is parsed by TypeScript tooling using a MIME
|
||||
envelope added at parse time.
|
||||
|
||||
The raw ``TODO`` file is not a MIME document. A preprocessor wraps it into a
|
||||
valid ``multipart/mixed`` MIME message before parsing.
|
||||
|
||||
|
||||
Raw File Structure
|
||||
==================
|
||||
|
||||
The raw ``TODO`` file consists of a sequence of *parts*, each beginning with::
|
||||
|
||||
--ISSUE
|
||||
Content-Type: <mime-type>
|
||||
|
||||
Valid part types are:
|
||||
|
||||
* ``application/sprints`` — defines sprint metadata
|
||||
* ``application/issue`` — defines a single issue
|
||||
|
||||
Parts may appear in any order in the raw file. The preprocessor will reorder
|
||||
them so that the ``application/sprints`` part appears first.
|
||||
|
||||
|
||||
MIME Envelope (Added by Preprocessor)
|
||||
=====================================
|
||||
|
||||
Before parsing, the preprocessor wraps the raw file into a MIME multipart
|
||||
message.
|
||||
|
||||
Prolog added by the preprocessor::
|
||||
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="ISSUE"
|
||||
|
||||
Each raw ``--ISSUE`` boundary becomes a MIME boundary. The preprocessor
|
||||
extracts each part's ``Content-Type`` and body, reorders parts, and emits a
|
||||
final closing boundary::
|
||||
|
||||
--ISSUE--
|
||||
|
||||
The resulting MIME message is parsed using a standard MIME parser.
|
||||
|
||||
|
||||
Part: ``application/sprints``
|
||||
=============================
|
||||
|
||||
This part defines sprint metadata.
|
||||
|
||||
The body MUST begin with::
|
||||
|
||||
Sprints:
|
||||
|
||||
followed by one or more sprint entries.
|
||||
|
||||
Sprint Entry Forms
|
||||
------------------
|
||||
|
||||
Both compact and expanded forms are valid.
|
||||
|
||||
Compact form::
|
||||
|
||||
- Name: Sprint 1
|
||||
Range: 2026-02-01..2026-02-14
|
||||
|
||||
Expanded form::
|
||||
|
||||
-
|
||||
Name: Sprint 1
|
||||
Range: 2026-02-01..2026-02-14
|
||||
|
||||
Sprint Entry Grammar
|
||||
--------------------
|
||||
|
||||
A sprint entry begins at a line matching::
|
||||
|
||||
^\s*-\s*(Name:.*)?$
|
||||
|
||||
Key–value lines inside an entry match::
|
||||
|
||||
^\s+[A-Za-z][A-Za-z0-9]*:\s*(.*)$
|
||||
|
||||
Required keys:
|
||||
|
||||
* ``Name: <string>``
|
||||
* ``Range: <YYYY-MM-DD>..<YYYY-MM-DD>``
|
||||
|
||||
The ``Range`` defines a closed interval ``[start, end]``.
|
||||
|
||||
|
||||
Part: ``application/issue``
|
||||
===========================
|
||||
|
||||
Each issue is represented as a structured block with strict field ordering.
|
||||
|
||||
Required Field Order
|
||||
--------------------
|
||||
|
||||
The following fields MUST appear in exactly this order::
|
||||
|
||||
ID: <integer>
|
||||
Type: <feature|bugfix|hotfix>
|
||||
Title: <short title>
|
||||
Status: <open|in-progress|done|hold|cancelled>
|
||||
Priority: <low|medium|high>
|
||||
Created: <YYYY-MM-DD>
|
||||
Relationships: <comma-separated list or empty>
|
||||
DueStart: <YYYY-MM-DD> # OPTIONAL
|
||||
DueEnd: <YYYY-MM-DD> # OPTIONAL
|
||||
Description: <first line>
|
||||
<continuation lines>
|
||||
|
||||
Field Semantics
|
||||
---------------
|
||||
|
||||
* **ID**: unique integer, strictly increasing.
|
||||
* **Type**: one of ``feature``, ``bugfix``, ``hotfix``.
|
||||
* **Status**: one of ``open``, ``in-progress``, ``done``, ``hold``, ``cancelled``.
|
||||
* **Priority**: one of ``low``, ``medium``, ``high``.
|
||||
* **Created**: ISO date.
|
||||
* **Relationships**:
|
||||
* Empty::
|
||||
|
||||
Relationships:
|
||||
|
||||
* Or list::
|
||||
|
||||
Relationships: dependsOn:43, relatesTo:10
|
||||
|
||||
* Valid kinds: ``dependsOn``, ``relatesTo``, ``blocks``.
|
||||
* **DueStart / DueEnd**:
|
||||
* Optional.
|
||||
* If only one is present, the other is implicitly equal to it.
|
||||
|
||||
Description Block
|
||||
-----------------
|
||||
|
||||
* The first line appears on the same line as ``Description:``.
|
||||
* Continuation lines:
|
||||
* Must begin with whitespace.
|
||||
* Must align indentation consistently.
|
||||
* No blank lines allowed inside the description block.
|
||||
|
||||
Body
|
||||
----
|
||||
|
||||
Any lines after the description block are considered free-form body text.
|
||||
The body MUST NOT contain another ``ID:`` field or a MIME boundary.
|
||||
|
||||
|
||||
Git Workflow Rules
|
||||
==================
|
||||
|
||||
Branch Naming
|
||||
-------------
|
||||
|
||||
Branches for issues MUST follow::
|
||||
|
||||
<Type>/<ID>
|
||||
|
||||
Examples::
|
||||
|
||||
feature/12
|
||||
bugfix/7
|
||||
|
||||
Modification Rules
|
||||
------------------
|
||||
|
||||
* All edits to ``TODO`` MUST occur on ``develop``.
|
||||
* Feature/bugfix/hotfix branches MUST rebase changes from ``develop``.
|
||||
|
||||
Branch Lifecycle
|
||||
----------------
|
||||
|
||||
* A branch MUST NOT exist before its issue is created.
|
||||
* A branch MUST be named exactly ``<Type>/<ID>``.
|
||||
* A branch MAY merge only when the issue status is ``done``.
|
||||
|
||||
|
||||
Sprint Membership Logic
|
||||
=======================
|
||||
|
||||
Given:
|
||||
|
||||
* Sprint interval ``[S_start, S_end]``
|
||||
* Issue interval ``[I_start, I_end]``
|
||||
|
||||
Normalization
|
||||
-------------
|
||||
|
||||
* Only ``DueEnd`` → ``I_start = I_end = DueEnd``
|
||||
* Only ``DueStart`` → ``I_start = I_end = DueStart``
|
||||
* Neither → issue is not sprint-bound
|
||||
|
||||
Membership Condition
|
||||
--------------------
|
||||
|
||||
An issue belongs to a sprint if::
|
||||
|
||||
I_start ≤ S_end AND I_end ≥ S_start
|
||||
|
||||
Current Sprint for Date D
|
||||
-------------------------
|
||||
|
||||
1. All sprints where ``S_start ≤ D ≤ S_end``.
|
||||
2. If multiple match, choose the one with the latest ``S_start``.
|
||||
3. If none match, no active sprint.
|
||||
|
||||
|
||||
Preprocessor Requirements
|
||||
=========================
|
||||
|
||||
The preprocessor MUST:
|
||||
|
||||
* Read the raw ``TODO`` file.
|
||||
* Split into parts using ``--ISSUE``.
|
||||
* Extract each part's ``Content-Type`` and body.
|
||||
* Reorder parts so that:
|
||||
* ``application/sprints`` appears first (if present)
|
||||
* All ``application/issue`` parts follow in original order
|
||||
* Emit a MIME message with:
|
||||
* ``MIME-Version: 1.0``
|
||||
* ``Content-Type: multipart/mixed; boundary="ISSUE"``
|
||||
* Each part wrapped as::
|
||||
|
||||
--ISSUE
|
||||
Content-Type: <type>
|
||||
|
||||
<body>
|
||||
|
||||
* Final boundary::
|
||||
|
||||
--ISSUE--
|
||||
|
||||
|
||||
Parser Requirements
|
||||
===================
|
||||
|
||||
The parser MUST:
|
||||
|
||||
* Use a MIME parser to extract parts.
|
||||
* Dispatch based on ``Content-Type``:
|
||||
* ``application/sprints`` → sprint parser
|
||||
* ``application/issue`` → issue parser
|
||||
* Enforce:
|
||||
* Field order
|
||||
* Required fields
|
||||
* Description indentation rules
|
||||
* Sprint entry grammar
|
||||
* Produce a ``TodoFile`` object::
|
||||
|
||||
{
|
||||
sprints: Sprint[],
|
||||
issues: Issue[]
|
||||
}
|
||||
|
||||
|
||||
Error Handling
|
||||
==============
|
||||
|
||||
The parser MUST reject:
|
||||
|
||||
* Missing or malformed ``Content-Type`` headers
|
||||
* Unknown MIME types
|
||||
* Missing required issue fields
|
||||
* Incorrect field order
|
||||
* Invalid sprint ranges
|
||||
* Invalid date formats
|
||||
* Multiple ``application/sprints`` parts
|
||||
* Duplicate issue IDs
|
||||
|
||||
Errors SHOULD include line numbers when possible.
|
||||
|
||||
|
||||
Extensibility
|
||||
=============
|
||||
|
||||
Future part types MAY be added using::
|
||||
|
||||
application/<subtype>
|
||||
|
||||
Examples:
|
||||
|
||||
* ``application/metadata``
|
||||
* ``application/changelog``
|
||||
* ``application/epilog``
|
||||
|
||||
The preprocessor MUST preserve unknown part types but MUST NOT reorder them.
|
||||
|
||||
Always determine first, whether the issue must immediately resolve on the
|
||||
production branch, or whether it can be resolved by introducing it through the
|
||||
development branch.
|
||||
|
||||
Ensure to switch to the branch impacted by the issue.
|
||||
|
||||
Add a new issue to the TODO file.
|
||||
|
||||
Issues first applied to the development branch
|
||||
MUST either be of type ``feature``, or ``bugfix``. Issues applied to the
|
||||
production branch MUST be of type ``hotfix``.
|
||||
|
||||
Define a
|
||||
62
src/conf.py
Normal file
62
src/conf.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import datetime
|
||||
from sphinx.util import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx_last_updated_by_git',
|
||||
'sphinx_markdown_builder',
|
||||
]
|
||||
|
||||
|
||||
templates_path = ["_templates"]
|
||||
|
||||
project = "MIME TODO"
|
||||
copyright = "2026, Tiara Rodney"
|
||||
|
||||
html_title = project
|
||||
html_theme = 'bizstyle'
|
||||
html_sidebars = {}
|
||||
html_show_sphinx = False
|
||||
html_show_sourcelink = False
|
||||
|
||||
html_context = {
|
||||
"bitbucket_url": "https://bitbucket.org",
|
||||
"bitbucket_user": "byteb4rb1e",
|
||||
"bitbucket_repo": "ai-mime-todo",
|
||||
"bitbucket_version": "master",
|
||||
"doc_path": "src/"
|
||||
}
|
||||
|
||||
|
||||
|
||||
language = 'en'
|
||||
|
||||
todo_include_todos = True
|
||||
|
||||
rst_prolog = f"""
|
||||
.. |build-time| replace:: {datetime.datetime.now().strftime("%d %B %Y, %H:%M")}
|
||||
"""
|
||||
|
||||
html_last_updated_fmt = "%d %B %Y, %H:%M"
|
||||
|
||||
root_doc = "README"
|
||||
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
def on_build_finished(app, exception):
|
||||
if exception is not None:
|
||||
return # skip on build failure
|
||||
|
||||
ilicense = Path(app.confdir) / '..' / 'LICENSE'
|
||||
olicense = Path(app.outdir) / 'LICENSE'
|
||||
|
||||
if ilicense.exists():
|
||||
shutil.copyfile(ilicense, olicense)
|
||||
logger.info(f"copied {ilicense} → {olicense}")
|
||||
|
||||
def setup(app):
|
||||
app.connect("build-finished", on_build_finished)
|
||||
Loading…
Add table
Add a link
Reference in a new issue