jearless 0.10.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jearless-0.10.4/.gitignore +220 -0
- jearless-0.10.4/.pre-commit-config.yaml +5 -0
- jearless-0.10.4/.version-hook.yml +49 -0
- jearless-0.10.4/CHANGELOG.md +17 -0
- jearless-0.10.4/LICENSE +21 -0
- jearless-0.10.4/PKG-INFO +141 -0
- jearless-0.10.4/PLAN.md +44 -0
- jearless-0.10.4/README.md +112 -0
- jearless-0.10.4/env.yml +4 -0
- jearless-0.10.4/pyproject.toml +55 -0
- jearless-0.10.4/snapshots/timeline-v0.10.4.mmd +14 -0
- jearless-0.10.4/snapshots/timeline-v0.9.4.mmd +13 -0
- jearless-0.10.4/src/jearless/__init__.py +12 -0
- jearless-0.10.4/src/jearless/add.py +122 -0
- jearless-0.10.4/src/jearless/changelog.py +81 -0
- jearless-0.10.4/src/jearless/cli.py +107 -0
- jearless-0.10.4/src/jearless/config.py +69 -0
- jearless-0.10.4/src/jearless/doctor.py +83 -0
- jearless-0.10.4/src/jearless/duplicate.py +86 -0
- jearless-0.10.4/src/jearless/errors.py +5 -0
- jearless-0.10.4/src/jearless/gitwrap.py +69 -0
- jearless-0.10.4/src/jearless/registry.py +105 -0
- jearless-0.10.4/src/jearless/release.py +83 -0
- jearless-0.10.4/src/jearless/retro.py +69 -0
- jearless-0.10.4/src/jearless/slug.py +28 -0
- jearless-0.10.4/src/jearless/sprint.py +33 -0
- jearless-0.10.4/src/jearless/state.py +86 -0
- jearless-0.10.4/src/jearless/take.py +63 -0
- jearless-0.10.4/src/jearless/timeline.py +121 -0
- jearless-0.10.4/tasks.yml +79 -0
- jearless-0.10.4/tests/conftest.py +39 -0
- jearless-0.10.4/tests/test_add.py +119 -0
- jearless-0.10.4/tests/test_changelog.py +83 -0
- jearless-0.10.4/tests/test_cli.py +40 -0
- jearless-0.10.4/tests/test_config.py +64 -0
- jearless-0.10.4/tests/test_doctor.py +76 -0
- jearless-0.10.4/tests/test_duplicate.py +47 -0
- jearless-0.10.4/tests/test_gitwrap.py +28 -0
- jearless-0.10.4/tests/test_registry.py +63 -0
- jearless-0.10.4/tests/test_release.py +91 -0
- jearless-0.10.4/tests/test_retro.py +80 -0
- jearless-0.10.4/tests/test_slug.py +19 -0
- jearless-0.10.4/tests/test_sprint.py +83 -0
- jearless-0.10.4/tests/test_take.py +64 -0
- jearless-0.10.4/tests/test_timeline.py +78 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
# jearless × auto-version-hook: the version lives in env.yml and rides the
|
|
3
|
+
# feature branch. The README invariant ("main moves only by merges") holds:
|
|
4
|
+
# main is protected, merge commits are skipped, the bump happens in the branch.
|
|
5
|
+
|
|
6
|
+
version:
|
|
7
|
+
file: env.yml
|
|
8
|
+
key: version
|
|
9
|
+
parse_regex: '^v(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)$'
|
|
10
|
+
|
|
11
|
+
# The package version mirrors the flow version (auto-version-hook v1.1.0):
|
|
12
|
+
# after every bump both move together; no v-prefix here (PEP 440 style).
|
|
13
|
+
version_files:
|
|
14
|
+
- path: pyproject.toml
|
|
15
|
+
regex: '^version = "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"$'
|
|
16
|
+
|
|
17
|
+
git:
|
|
18
|
+
remote: origin
|
|
19
|
+
fetch: true
|
|
20
|
+
|
|
21
|
+
branch_flow:
|
|
22
|
+
- name: main
|
|
23
|
+
regex: '^main$'
|
|
24
|
+
protected: true # a direct commit to main never bumps the version
|
|
25
|
+
policy: keep
|
|
26
|
+
|
|
27
|
+
- name: feature
|
|
28
|
+
regex: '^feature/' # feature/<project>/<id>-<slug> — the whole README taxonomy
|
|
29
|
+
target: main
|
|
30
|
+
policy: increment
|
|
31
|
+
bump: minor
|
|
32
|
+
reset_lower_parts: false
|
|
33
|
+
|
|
34
|
+
- name: fix
|
|
35
|
+
regex: '^fix/'
|
|
36
|
+
target: main
|
|
37
|
+
policy: increment
|
|
38
|
+
bump: patch
|
|
39
|
+
reset_lower_parts: false
|
|
40
|
+
|
|
41
|
+
- name: chore
|
|
42
|
+
regex: '^chore/'
|
|
43
|
+
target: main
|
|
44
|
+
policy: ignore
|
|
45
|
+
|
|
46
|
+
hooks:
|
|
47
|
+
auto_stage: true
|
|
48
|
+
on_error: fail
|
|
49
|
+
skip_merges: true # does not act on merge commits — the charter moves those
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
## v0.10.4 (2026-11-07)
|
|
3
|
+
### Added
|
|
4
|
+
- tooling-10: add — duplicate guard: keyword-overlap warning and confirmation
|
|
5
|
+
# 1 off-taxonomy merge(s) skipped — the visible hole (README)
|
|
6
|
+
|
|
7
|
+
## v0.9.4 (2026-11-07)
|
|
8
|
+
### Added
|
|
9
|
+
- tooling-1: add — append a task to the registry
|
|
10
|
+
- tooling-2: take — claim a task with a single push
|
|
11
|
+
- tooling-3: sprint — a join of three sources
|
|
12
|
+
- tooling-4: changelog — a generator over merge commits
|
|
13
|
+
- tooling-5: doctor — the visible hole
|
|
14
|
+
- tooling-6: timeline — live and snapshot
|
|
15
|
+
- tooling-7: retro — plan versus actual
|
|
16
|
+
- tooling-9: release step — tag, changelog and timeline snapshot in one move
|
|
17
|
+
# 8 off-taxonomy merge(s) skipped — the visible hole (README)
|
jearless-0.10.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AndrewZakh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jearless-0.10.4/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: jearless
|
|
3
|
+
Version: 0.10.4
|
|
4
|
+
Summary: Fearless. Jearless. Task tracking and a changelog on bare git — for small teams.
|
|
5
|
+
Project-URL: Homepage, https://github.com/AndrewZakh/jearless
|
|
6
|
+
Project-URL: Repository, https://github.com/AndrewZakh/jearless
|
|
7
|
+
Project-URL: Changelog, https://github.com/AndrewZakh/jearless/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/AndrewZakh/jearless/issues
|
|
9
|
+
Author: Andrei Zakhrebetkov
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: changelog,cli,git,issue-tracker,task-tracking
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: ruamel-yaml>=0.18
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pre-commit>=3; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# jearless
|
|
31
|
+
|
|
32
|
+
**Fearless. Jearless.** Task tracking and a changelog on bare git — for small teams.
|
|
33
|
+
|
|
34
|
+
*Why the name: by ear — a faint echo of Jira; by sight — fearless; spelled with an error — and we track errors for a living; a jearless sprint — a fast run without fat.*
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
From PyPI, straight into an isolated environment:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pipx install jearless # the intended way for daily use
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For development, one command builds the venv with everything. There is
|
|
45
|
+
deliberately **no requirements.txt**: `pyproject.toml` is the single authored
|
|
46
|
+
source of dependencies, and a second list would drift.
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
uv venv && uv pip install -e ".[dev]" # uv
|
|
50
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" # plain pip
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
|
|
55
|
+
Inside your git repository:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
jear add door "paint it black" --points 2 # a task in the eternal registry
|
|
59
|
+
jear take door 1 # branch feature/door/1-paint-it-black, pushed
|
|
60
|
+
# ... work, then a true merge (--no-ff) ...
|
|
61
|
+
jear sprint # available / in_progress / done — derived from git
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
No statuses are ever stored: the registry holds frozen facts, git holds
|
|
65
|
+
everything alive. See [The idea](#the-idea) for the full model.
|
|
66
|
+
|
|
67
|
+
**Status:** the design has stabilized; the mechanism is built around the eternal registry; the name is settled — jearless (2026-11-06). This file records the decisions and their rationale — it is not a specification. The implementation plan lives in [PLAN.md](PLAN.md).
|
|
68
|
+
|
|
69
|
+
## The idea
|
|
70
|
+
|
|
71
|
+
Task tracking and a changelog on bare git. No trackers, no infrastructure.
|
|
72
|
+
|
|
73
|
+
1. `tasks.yml` — the **eternal task registry**: subproject, id, summary, description, story_points. Tasks are added and never deleted; the file only grows. No live-state fields.
|
|
74
|
+
2. `jear take door_design 16` — zero commits, zero YAML edits: verify the branch is absent on origin, create `feature/door_design/16-slug`, push. **Taking a task = pushing the branch.**
|
|
75
|
+
3. Merging is a true merge (`--no-ff`): the branch name lands in history through the merge commit. No squash, no rebase merges.
|
|
76
|
+
4. The changelog is not stored — it is generated at release time: merge commits between tags → the regex from the config → the line template. The line is the summary from the registry or the slug from the branch name.
|
|
77
|
+
5. The branch prefix (`feature/`, `fix/`, `chore/`) → the changelog section.
|
|
78
|
+
|
|
79
|
+
## The state model: three sources, zero stored statuses
|
|
80
|
+
|
|
81
|
+
| State | Condition |
|
|
82
|
+
|---|---|
|
|
83
|
+
| available | present in tasks.yml ∧ no branch on origin ∧ no merge commit |
|
|
84
|
+
| in_progress | a branch exists on origin ∧ no merge commit |
|
|
85
|
+
| done | a merge commit carrying the branch name exists in main (overrides an existing branch when the forge's auto-delete is off) |
|
|
86
|
+
|
|
87
|
+
The sprint and all analytics are joins on the key (subproject, number), computed on demand. The race for a task is arbitrated by git itself: a second push of a branch with the same name is rejected as non-fast-forward — "taken" arrives from the server.
|
|
88
|
+
|
|
89
|
+
## Timeline and plan-versus-actual
|
|
90
|
+
|
|
91
|
+
- **The actual timeline** is a derived view: start = author date of the branch's first commit, end = committer date of the merge commit. The live variant is a command run on demand (fetch --prune, open branches end "now") and is **never committed**. The release snapshot is frozen by the tag and committed next to the changelog.
|
|
92
|
+
- **The Gantt (the plan)** is an authored artifact: drawn by hand or by script, committed as the team's contract. For the retrospective comparison, **bar ids = task ids** (subproject + number) — otherwise the plan cannot be joined with the facts.
|
|
93
|
+
- The hierarchy of truth about the start: the branch's first commit (exact, eternal) → merge-base (skewed by main's stagnation: the common commit's date ≠ the cut date) → forge events (the push time is visible, but the feed expires — on GitHub, within about 90 days). Push time does not exist in git's object model.
|
|
94
|
+
- Activity time metrics (hours, commit-clock deadlines) are deliberately out of scope: commit dates are client clocks, trivially spoofed, and they invite gaming.
|
|
95
|
+
|
|
96
|
+
## Key decisions
|
|
97
|
+
|
|
98
|
+
- **The number is an identifier, not a position.** No renumbering, no reuse; the uniqueness key is the pair (subproject, number). Allocating the next number looks only at the registry — it never empties and never shrinks.
|
|
99
|
+
- **The eternal registry instead of deletion:** the old mechanics — "the task's deletion rides the branch's first commit" — is cancelled. Consequences: the task card (story_points included) lives forever, so analytics over done tasks lose nothing; `take` never mutates the YAML, so the yq round-trip problem disappears; registry conflicts become nearly impossible — only simultaneous additions at the same spot.
|
|
100
|
+
- **The branch name is the only key** linking the registry, the changelog and history. Slashes are the structural separators (`feature/door_design/16-slug`); hyphens live only inside slugs.
|
|
101
|
+
- **YAML as the registry format:** readable by yq, analytics over frozen facts. Live-state fields (status and friends) are forbidden: git knows the state, a stored copy drifts. Scripts only read the registry (`add` appending is the sole sanctioned exception).
|
|
102
|
+
- **The language of the registry, slugs and changelog is English** (the GitHub norm; decided 2026-11-06). Transliteration is only a fallback inside `take`, for summaries not written in latin script — a last resort for those who skipped their lessons, not the project's style.
|
|
103
|
+
- **main moves by merges only — no squash, no rebase merges, always `--no-ff`.** Direct human commits (add a task, fix the plan) are allowed; the ban concerns automatic writers and merge methods. Rebasing inside one's own unmerged branch is harmless — author dates survive it.
|
|
104
|
+
- **Nothing automatic writes to main.** Dropping a task = deleting the branch = the task is available again (it never left the registry).
|
|
105
|
+
- **"A separate life for the registry" is rejected** (a submodule, a database rendered into yml, a secrets manager): moving the storage does not solve the trigger problem — who flips `done` after the merge. The principle: **state for an event that cannot be observed without infrastructure is never stored — it is derived.**
|
|
106
|
+
- **Taking a task is a branch push: an occupancy registry, not a notification** — others see it at their own fetch.
|
|
107
|
+
|
|
108
|
+
## Pros
|
|
109
|
+
|
|
110
|
+
- One database — git history plus the eternal registry of facts. Zero state files, zero hooks with write access, zero CI jobs pushing to main.
|
|
111
|
+
- Taking a task is one push, zero commits; undoing it is deleting the branch.
|
|
112
|
+
- The task card lives in the registry forever: velocity and any analytics over done tasks lose no data.
|
|
113
|
+
- The race for a task is resolved by the server, atomically, without locks or queues.
|
|
114
|
+
- Plan-versus-actual from two honest sources: the plan is authored and committed, the actual is derived from git, the comparison is the retrospective.
|
|
115
|
+
- Branch prefixes are the taxonomy: "Added / Fixed" sections come for free.
|
|
116
|
+
- Merge commits make a task an object of history: `--merges` between tags is the task journal; a merge commit's diff is the whole task at once.
|
|
117
|
+
- Version binding is free: whatever is merged between v1.2 and v1.3 lands under the v1.3 heading.
|
|
118
|
+
- The registry is versioned with the code, reviewed in merges, available offline.
|
|
119
|
+
|
|
120
|
+
## Cons / risks
|
|
121
|
+
|
|
122
|
+
- **Squash and rebase merges silently lose the branch name** — the changelog line disappears without an error. On the forge these are the two enemy buttons; the repo needs "Create a merge commit" as the default. The legitimate way to clean up a task's history is **squash inside the branch before the true merge** ("squash inside, merge outside"): compress the WIP into one commit preserving the first commit's author date, then `--no-ff`. Verified by a live demo, 2026-11-06: the key lives in the merge commit, the timeline start in the preserved author date.
|
|
123
|
+
- **Fast-forward is the default** and the sneakiest source of lost merge commits: when main has not moved, git just moves the pointer. The "always --no-ff" rule is easy to forget.
|
|
124
|
+
- **The regex in the config rots first** (the very first draft failed to match its own example). A generator dry-run is mandatory.
|
|
125
|
+
- The regex must digest both merge message forms: "Merge branch ..." and "Merge pull request #N from owner/...".
|
|
126
|
+
- **YAML under hand editing:** block scalars for long descriptions are the most finicky spot; on-the-fly validation is mandatory.
|
|
127
|
+
- **Registry growth:** the file only increases. A measurement (2026-11-06, synthetic, ~270 bytes per task): a filtered query over 1M tasks (270 MB) takes 20 s with slow ruby/libyaml and 2 s with jq; a full minute arrives at ~3–30 million tasks — outside human scales. The real archivation trigger is ergonomics (scrolling the file, bloated diffs), not query speed.
|
|
128
|
+
- **available is a three-way join with negation** (in the registry ∧ no branch ∧ no merge commit): easy to get wrong — hence exactly one canonical implementation.
|
|
129
|
+
- **The start of a bar ≠ the birth of the branch:** merge-base is skewed by main's stagnation; the forge event feed expires; git does not store push time. The only eternal starting point is the branch's first commit.
|
|
130
|
+
- **Local origin/* refs are a cache, not the truth:** without `fetch --prune`, ghosts of deleted branches keep tasks "taken" forever.
|
|
131
|
+
- Only single-line changelog entries; multi-paragraph ones are a backup path of towncrier-style fragments.
|
|
132
|
+
- The insurance against someone else's squash/rebase is **a task trailer in the commit message** (`Task: door_design/16`; modern gits even have `git commit --trailer`): it rides in the object itself and survives a squash if it made it into the squash message. git notes and refs/replace were rejected as metadata channels: they do not travel with a default clone/fetch and forges do not show them. When a squash has already happened, the key cannot be attached retroactively (amend would rewrite main) — which is why the generator's dry-run must highlight unattributed commits loudly: the hole must be visible, not silent.
|
|
133
|
+
- The applicability boundary: the moment you need labels, assignees or task discussion — move to Issues, do not build a hybrid. Jira earns its keep for large teams and corporate scale; jearless is for the small and the swift.
|
|
134
|
+
|
|
135
|
+
## Open questions
|
|
136
|
+
|
|
137
|
+
- The generator config: the regex + the line template + the prefix-to-section mapping; the dry-run format.
|
|
138
|
+
- How to hard-wire the fast-forward ban (an alias, a setting, a check inside `take`).
|
|
139
|
+
- Branches that miss the regex (quick fixes without a task) — a filter feature or a hole?
|
|
140
|
+
- Auto-deleting branches on the forge after a merge — the generator must not rely on the branch still existing.
|
|
141
|
+
- Archiving `tasks.yml`: the trigger is ergonomic discomfort, not performance. Rules for the future: cut by release tag (the same frozen moment as the changelog and the timeline snapshot), the file `archive_<tag>.yml`, move out done tasks only; available/in_progress are always computed over tasks.yml, identity for analytics is the union of the registry and the archives; it is a release-time step run by a human. The script itself is a registry task, to be taken when the trigger fires.
|
jearless-0.10.4/PLAN.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Implementation plan
|
|
2
|
+
|
|
3
|
+
Date: 2026-11-06. The design lives in README.md (the project constitution — argued and proven). This file is how we build it. The name is settled: **jearless**. Package `jearless`, console command `jear` (alias `jearless`), config `.jearless.toml`. Slogan: "Fearless. Jearless. Bare git is enough — for small teams."
|
|
4
|
+
|
|
5
|
+
## Technical decisions (and why)
|
|
6
|
+
|
|
7
|
+
- **Python 3.11+, installed via pipx.** One dependency — ruamel.yaml. Everything else is stdlib.
|
|
8
|
+
- **Git via subprocess.** Zero dependencies and full flag transparency. GitPython drags in gitdb and hides parameters — and our tool is precisely about the exact semantics of git; hiding it is out of the question.
|
|
9
|
+
- **Versioning via auto-version-hook** (pre-commit, pinned `v1.1.0`, config `.version-hook.yml`): the version bump rides the feature branch, main is protected, merge commits are untouched (`skip_merges`) — the "main moves only by merges" invariant holds. The flow version lives in `env.yml`; the package version in pyproject mirrors it automatically (`version_files`, hook v1.1.0).
|
|
10
|
+
- **CLI on argparse.** The tool preaches minimalism — let the framework count be zero.
|
|
11
|
+
- **ruamel.yaml (rt mode) — the only YAML parser.** Edits preserve comments (closes the round-trip risk from the README); reads use it too — the files are kilobytes, and the 2026-11-06 measurement showed performance is a non-issue at any scale.
|
|
12
|
+
- **Tests are repository factories in temporary directories** (the pattern of our /tmp demos) plus golden files for the generator. No git mocks — only real repositories with real merges.
|
|
13
|
+
- **The config is `.jearless.toml`:** the branch regex, the changelog line template, the prefix-to-section mapping.
|
|
14
|
+
|
|
15
|
+
## The CLI surface (mapped to the README)
|
|
16
|
+
|
|
17
|
+
| Command | Mechanics | Phase |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `add <project> "summary" [--points N] [--yes]` | append a task to the registry (comments survive); next id = max over registry ∧ archives ∧ branches; the duplicate guard warns on keyword overlap and asks `y/N` (non-TTY declines, `--yes` skips) | 1 |
|
|
20
|
+
| `take <project> <id>` | registry → slug; check origin (ls-remote — the truth, not the cache); branch `feature/<project>/<id>-<slug>`; push. The server arbitrates the race | 1 |
|
|
21
|
+
| `sprint` | fetch --prune; the join registry × origin branches × merge log; states available / in_progress / done | 1 |
|
|
22
|
+
| `changelog --from <tag> --to <tag> --dry-run` | merge commits → the config regex → sections by prefix; the summary from the registry (union with archives) | 2 |
|
|
23
|
+
| `doctor` | unattributed commits on main (neither merges nor registry edits) — the visible hole; branch naming violations; an invalid registry | 2 |
|
|
24
|
+
| `timeline --live \| --snapshot <tag>` | a mermaid gantt of the facts; live is never committed; the snapshot is a release artifact | 3 |
|
|
25
|
+
| `retro <plan.md>` | join the plan's bar ids (the gantt contract) with the actual timeline; planning misses | 3 |
|
|
26
|
+
| `archive <tag>` | move done tasks into `archive_<tag>.yml` — only on the ergonomics trigger | 4 |
|
|
27
|
+
| `release [--tag TAG]` | tag + changelog section + timeline snapshot in one human-run move; the tag defaults to the env.yml flow version; artifacts are committed and the annotated tag lands on them | — |
|
|
28
|
+
|
|
29
|
+
## Phases and Definitions of Done
|
|
30
|
+
|
|
31
|
+
- **Phase 0. Skeleton:** pyproject, the CLI frame, the config schema, the git wrapper (subprocess), lint. DoD: `--help` works; empty states (no tags, no branches, an empty registry) do not crash commands.
|
|
32
|
+
- **Phase 1. The core loop:** add + take + sprint. DoD: a task is taken with a single push; the sprint is assembled from three sources; the race test — two takes from two clones against one remote, the second one gets "taken"; deleting a branch returns the task to available.
|
|
33
|
+
- **Phase 2. The journal:** changelog + doctor. DoD: golden tests on a factory repository with both merge message forms ("Merge branch …" and "Merge pull request …"); a squash commit is highlighted by doctor; dry-run is the default mode.
|
|
34
|
+
- **Phase 3. Time:** timeline live/snapshot + retro. DoD: the snapshot carries the author date of the branch's first commit and the committer date of the merge; a plan with task ids joins with the facts.
|
|
35
|
+
- **Phase 4. Deferred (on trigger):** archive, velocity analytics over the union of the registry and the archives.
|
|
36
|
+
|
|
37
|
+
## What is deliberately absent (and that is a feature)
|
|
38
|
+
|
|
39
|
+
Statuses and assignees (derived, not stored), CI integrations, a web UI, daemons, hooks, notifications. The moment those are needed — time to leave for Issues / git-bug / Backlog.md, not to build them in here.
|
|
40
|
+
|
|
41
|
+
## Open implementation questions
|
|
42
|
+
|
|
43
|
+
- `take` for a task missing from the registry: refuse, or an `--adhoc` flag — this would also settle the "branches off the regex" question.
|
|
44
|
+
- Detecting a fast-forward retroactively is impossible (it leaves no trace) — doctor catches only unattributed commits.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# jearless
|
|
2
|
+
|
|
3
|
+
**Fearless. Jearless.** Task tracking and a changelog on bare git — for small teams.
|
|
4
|
+
|
|
5
|
+
*Why the name: by ear — a faint echo of Jira; by sight — fearless; spelled with an error — and we track errors for a living; a jearless sprint — a fast run without fat.*
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
From PyPI, straight into an isolated environment:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pipx install jearless # the intended way for daily use
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For development, one command builds the venv with everything. There is
|
|
16
|
+
deliberately **no requirements.txt**: `pyproject.toml` is the single authored
|
|
17
|
+
source of dependencies, and a second list would drift.
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
uv venv && uv pip install -e ".[dev]" # uv
|
|
21
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" # plain pip
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
Inside your git repository:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
jear add door "paint it black" --points 2 # a task in the eternal registry
|
|
30
|
+
jear take door 1 # branch feature/door/1-paint-it-black, pushed
|
|
31
|
+
# ... work, then a true merge (--no-ff) ...
|
|
32
|
+
jear sprint # available / in_progress / done — derived from git
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
No statuses are ever stored: the registry holds frozen facts, git holds
|
|
36
|
+
everything alive. See [The idea](#the-idea) for the full model.
|
|
37
|
+
|
|
38
|
+
**Status:** the design has stabilized; the mechanism is built around the eternal registry; the name is settled — jearless (2026-11-06). This file records the decisions and their rationale — it is not a specification. The implementation plan lives in [PLAN.md](PLAN.md).
|
|
39
|
+
|
|
40
|
+
## The idea
|
|
41
|
+
|
|
42
|
+
Task tracking and a changelog on bare git. No trackers, no infrastructure.
|
|
43
|
+
|
|
44
|
+
1. `tasks.yml` — the **eternal task registry**: subproject, id, summary, description, story_points. Tasks are added and never deleted; the file only grows. No live-state fields.
|
|
45
|
+
2. `jear take door_design 16` — zero commits, zero YAML edits: verify the branch is absent on origin, create `feature/door_design/16-slug`, push. **Taking a task = pushing the branch.**
|
|
46
|
+
3. Merging is a true merge (`--no-ff`): the branch name lands in history through the merge commit. No squash, no rebase merges.
|
|
47
|
+
4. The changelog is not stored — it is generated at release time: merge commits between tags → the regex from the config → the line template. The line is the summary from the registry or the slug from the branch name.
|
|
48
|
+
5. The branch prefix (`feature/`, `fix/`, `chore/`) → the changelog section.
|
|
49
|
+
|
|
50
|
+
## The state model: three sources, zero stored statuses
|
|
51
|
+
|
|
52
|
+
| State | Condition |
|
|
53
|
+
|---|---|
|
|
54
|
+
| available | present in tasks.yml ∧ no branch on origin ∧ no merge commit |
|
|
55
|
+
| in_progress | a branch exists on origin ∧ no merge commit |
|
|
56
|
+
| done | a merge commit carrying the branch name exists in main (overrides an existing branch when the forge's auto-delete is off) |
|
|
57
|
+
|
|
58
|
+
The sprint and all analytics are joins on the key (subproject, number), computed on demand. The race for a task is arbitrated by git itself: a second push of a branch with the same name is rejected as non-fast-forward — "taken" arrives from the server.
|
|
59
|
+
|
|
60
|
+
## Timeline and plan-versus-actual
|
|
61
|
+
|
|
62
|
+
- **The actual timeline** is a derived view: start = author date of the branch's first commit, end = committer date of the merge commit. The live variant is a command run on demand (fetch --prune, open branches end "now") and is **never committed**. The release snapshot is frozen by the tag and committed next to the changelog.
|
|
63
|
+
- **The Gantt (the plan)** is an authored artifact: drawn by hand or by script, committed as the team's contract. For the retrospective comparison, **bar ids = task ids** (subproject + number) — otherwise the plan cannot be joined with the facts.
|
|
64
|
+
- The hierarchy of truth about the start: the branch's first commit (exact, eternal) → merge-base (skewed by main's stagnation: the common commit's date ≠ the cut date) → forge events (the push time is visible, but the feed expires — on GitHub, within about 90 days). Push time does not exist in git's object model.
|
|
65
|
+
- Activity time metrics (hours, commit-clock deadlines) are deliberately out of scope: commit dates are client clocks, trivially spoofed, and they invite gaming.
|
|
66
|
+
|
|
67
|
+
## Key decisions
|
|
68
|
+
|
|
69
|
+
- **The number is an identifier, not a position.** No renumbering, no reuse; the uniqueness key is the pair (subproject, number). Allocating the next number looks only at the registry — it never empties and never shrinks.
|
|
70
|
+
- **The eternal registry instead of deletion:** the old mechanics — "the task's deletion rides the branch's first commit" — is cancelled. Consequences: the task card (story_points included) lives forever, so analytics over done tasks lose nothing; `take` never mutates the YAML, so the yq round-trip problem disappears; registry conflicts become nearly impossible — only simultaneous additions at the same spot.
|
|
71
|
+
- **The branch name is the only key** linking the registry, the changelog and history. Slashes are the structural separators (`feature/door_design/16-slug`); hyphens live only inside slugs.
|
|
72
|
+
- **YAML as the registry format:** readable by yq, analytics over frozen facts. Live-state fields (status and friends) are forbidden: git knows the state, a stored copy drifts. Scripts only read the registry (`add` appending is the sole sanctioned exception).
|
|
73
|
+
- **The language of the registry, slugs and changelog is English** (the GitHub norm; decided 2026-11-06). Transliteration is only a fallback inside `take`, for summaries not written in latin script — a last resort for those who skipped their lessons, not the project's style.
|
|
74
|
+
- **main moves by merges only — no squash, no rebase merges, always `--no-ff`.** Direct human commits (add a task, fix the plan) are allowed; the ban concerns automatic writers and merge methods. Rebasing inside one's own unmerged branch is harmless — author dates survive it.
|
|
75
|
+
- **Nothing automatic writes to main.** Dropping a task = deleting the branch = the task is available again (it never left the registry).
|
|
76
|
+
- **"A separate life for the registry" is rejected** (a submodule, a database rendered into yml, a secrets manager): moving the storage does not solve the trigger problem — who flips `done` after the merge. The principle: **state for an event that cannot be observed without infrastructure is never stored — it is derived.**
|
|
77
|
+
- **Taking a task is a branch push: an occupancy registry, not a notification** — others see it at their own fetch.
|
|
78
|
+
|
|
79
|
+
## Pros
|
|
80
|
+
|
|
81
|
+
- One database — git history plus the eternal registry of facts. Zero state files, zero hooks with write access, zero CI jobs pushing to main.
|
|
82
|
+
- Taking a task is one push, zero commits; undoing it is deleting the branch.
|
|
83
|
+
- The task card lives in the registry forever: velocity and any analytics over done tasks lose no data.
|
|
84
|
+
- The race for a task is resolved by the server, atomically, without locks or queues.
|
|
85
|
+
- Plan-versus-actual from two honest sources: the plan is authored and committed, the actual is derived from git, the comparison is the retrospective.
|
|
86
|
+
- Branch prefixes are the taxonomy: "Added / Fixed" sections come for free.
|
|
87
|
+
- Merge commits make a task an object of history: `--merges` between tags is the task journal; a merge commit's diff is the whole task at once.
|
|
88
|
+
- Version binding is free: whatever is merged between v1.2 and v1.3 lands under the v1.3 heading.
|
|
89
|
+
- The registry is versioned with the code, reviewed in merges, available offline.
|
|
90
|
+
|
|
91
|
+
## Cons / risks
|
|
92
|
+
|
|
93
|
+
- **Squash and rebase merges silently lose the branch name** — the changelog line disappears without an error. On the forge these are the two enemy buttons; the repo needs "Create a merge commit" as the default. The legitimate way to clean up a task's history is **squash inside the branch before the true merge** ("squash inside, merge outside"): compress the WIP into one commit preserving the first commit's author date, then `--no-ff`. Verified by a live demo, 2026-11-06: the key lives in the merge commit, the timeline start in the preserved author date.
|
|
94
|
+
- **Fast-forward is the default** and the sneakiest source of lost merge commits: when main has not moved, git just moves the pointer. The "always --no-ff" rule is easy to forget.
|
|
95
|
+
- **The regex in the config rots first** (the very first draft failed to match its own example). A generator dry-run is mandatory.
|
|
96
|
+
- The regex must digest both merge message forms: "Merge branch ..." and "Merge pull request #N from owner/...".
|
|
97
|
+
- **YAML under hand editing:** block scalars for long descriptions are the most finicky spot; on-the-fly validation is mandatory.
|
|
98
|
+
- **Registry growth:** the file only increases. A measurement (2026-11-06, synthetic, ~270 bytes per task): a filtered query over 1M tasks (270 MB) takes 20 s with slow ruby/libyaml and 2 s with jq; a full minute arrives at ~3–30 million tasks — outside human scales. The real archivation trigger is ergonomics (scrolling the file, bloated diffs), not query speed.
|
|
99
|
+
- **available is a three-way join with negation** (in the registry ∧ no branch ∧ no merge commit): easy to get wrong — hence exactly one canonical implementation.
|
|
100
|
+
- **The start of a bar ≠ the birth of the branch:** merge-base is skewed by main's stagnation; the forge event feed expires; git does not store push time. The only eternal starting point is the branch's first commit.
|
|
101
|
+
- **Local origin/* refs are a cache, not the truth:** without `fetch --prune`, ghosts of deleted branches keep tasks "taken" forever.
|
|
102
|
+
- Only single-line changelog entries; multi-paragraph ones are a backup path of towncrier-style fragments.
|
|
103
|
+
- The insurance against someone else's squash/rebase is **a task trailer in the commit message** (`Task: door_design/16`; modern gits even have `git commit --trailer`): it rides in the object itself and survives a squash if it made it into the squash message. git notes and refs/replace were rejected as metadata channels: they do not travel with a default clone/fetch and forges do not show them. When a squash has already happened, the key cannot be attached retroactively (amend would rewrite main) — which is why the generator's dry-run must highlight unattributed commits loudly: the hole must be visible, not silent.
|
|
104
|
+
- The applicability boundary: the moment you need labels, assignees or task discussion — move to Issues, do not build a hybrid. Jira earns its keep for large teams and corporate scale; jearless is for the small and the swift.
|
|
105
|
+
|
|
106
|
+
## Open questions
|
|
107
|
+
|
|
108
|
+
- The generator config: the regex + the line template + the prefix-to-section mapping; the dry-run format.
|
|
109
|
+
- How to hard-wire the fast-forward ban (an alias, a setting, a check inside `take`).
|
|
110
|
+
- Branches that miss the regex (quick fixes without a task) — a filter feature or a hole?
|
|
111
|
+
- Auto-deleting branches on the forge after a merge — the generator must not rely on the branch still existing.
|
|
112
|
+
- Archiving `tasks.yml`: the trigger is ergonomic discomfort, not performance. Rules for the future: cut by release tag (the same frozen moment as the changelog and the timeline snapshot), the file `archive_<tag>.yml`, move out done tasks only; available/in_progress are always computed over tasks.yml, identity for analytics is the union of the registry and the archives; it is a release-time step run by a human. The script itself is a registry task, to be taken when the trigger fires.
|
jearless-0.10.4/env.yml
ADDED