prodc 0.3.63__tar.gz → 0.3.64__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.
- {prodc-0.3.63 → prodc-0.3.64}/PKG-INFO +1 -1
- {prodc-0.3.63 → prodc-0.3.64}/pyproject.toml +1 -1
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/cli.py +3 -5
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/model.py +37 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/status.py +1 -1
- {prodc-0.3.63 → prodc-0.3.64}/.gitignore +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/CHANGELOG.md +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/LICENSE +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/README.md +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/examples/quickstart/features/orders.feature +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/examples/quickstart/needs/jordan.yaml +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/examples/quickstart/prodc.toml +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/__init__.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/adapters.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/agent.md +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/board.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/callbacks.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/config.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/dedup.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/loader.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/py.typed +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/report.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/report_html.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/src/prodc/writer.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_adapters.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_callbacks.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_cli.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_command_hook.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_dedup.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_description.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_drafts.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_external_adapter.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_gitlab_source.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_importance.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_loader.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_models.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_report.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_report_html.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_roles.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_sources.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_staleness.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_status.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_suggest.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_variants.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_who_variants.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_writer.py +0 -0
- {prodc-0.3.63 → prodc-0.3.64}/tests/test_writer_comments.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: prodc
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.64
|
|
4
4
|
Summary: Product management as code: roles, flows, and features traced to evidence.
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/jorgeecardona/prodc
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/jorgeecardona/prodc
|
|
@@ -49,6 +49,7 @@ from .model import (
|
|
|
49
49
|
NeedState,
|
|
50
50
|
Release,
|
|
51
51
|
Role,
|
|
52
|
+
board_needs,
|
|
52
53
|
ears_shape,
|
|
53
54
|
)
|
|
54
55
|
from .report import render_markdown
|
|
@@ -541,8 +542,7 @@ def _cmd_status(args: argparse.Namespace) -> int:
|
|
|
541
542
|
# Draft needs (#60) are staged: kept off the counted board (and coverage, recurrence,
|
|
542
543
|
# orphans) until promoted with `set <id> draft=false`, unless --include-drafts is passed.
|
|
543
544
|
draft_count = sum(1 for n in needs if n.draft)
|
|
544
|
-
|
|
545
|
-
needs = [n for n in needs if not n.draft]
|
|
545
|
+
needs = board_needs(needs, include_drafts=args.include_drafts)
|
|
546
546
|
# Scoped coverage (#49): restrict the board + coverage to a subset, so the number people
|
|
547
547
|
# act on (this client, this goal) isn't diluted by the whole mixed board.
|
|
548
548
|
if args.client:
|
|
@@ -858,9 +858,7 @@ def _cmd_plan(args: argparse.Namespace) -> int:
|
|
|
858
858
|
if loaded is None:
|
|
859
859
|
return 2
|
|
860
860
|
cfg, model = loaded
|
|
861
|
-
needs = list(model.needs)
|
|
862
|
-
if not args.include_drafts:
|
|
863
|
-
needs = [n for n in needs if not n.draft] # drafts aren't backlog until promoted (#60)
|
|
861
|
+
needs = board_needs(list(model.needs), include_drafts=args.include_drafts) # drafts off (#60)
|
|
864
862
|
results = resolve(needs, cfg.adapters, cfg.source_adapters)
|
|
865
863
|
recurrence_counts = {rec.baseline.id: rec.count for rec in recurrence(needs)}
|
|
866
864
|
print(
|
|
@@ -194,6 +194,43 @@ class Need:
|
|
|
194
194
|
kinds = {s.kind for s in self.sources}
|
|
195
195
|
return next((k for k in order if k in kinds), None)
|
|
196
196
|
|
|
197
|
+
@property
|
|
198
|
+
def counts_toward_coverage(self) -> bool:
|
|
199
|
+
"""Whether this need is a unit of product coverage — what :func:`~prodc.status.summarize`
|
|
200
|
+
counts.
|
|
201
|
+
|
|
202
|
+
A leaf product need. A flow (a need with ``children``) is a roll-up of its leaves, so
|
|
203
|
+
counting it too would double-count (#43); a non-product need (infra/NFR/internal) is
|
|
204
|
+
excluded from product coverage (#48). Draft staging is a separate, upstream filter
|
|
205
|
+
(:func:`board_needs`), not part of this rule — so a draft still counts once promoted onto
|
|
206
|
+
the board (e.g. under ``--include-drafts``).
|
|
207
|
+
"""
|
|
208
|
+
return not self.children and self.product
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def excluded_reason(self) -> str | None:
|
|
212
|
+
"""Why this need is off the counted board, or ``None`` when it is on it.
|
|
213
|
+
|
|
214
|
+
The single home for the lifecycle exclusions, so a new state lands here once: ``"draft"``
|
|
215
|
+
(staged, not yet promoted, #60) and ``"nonproduct"`` (infra/NFR/internal, #48). A flow is
|
|
216
|
+
not excluded — it is a roll-up row on the board — so ``children`` is not a reason here
|
|
217
|
+
(that axis is :attr:`counts_toward_coverage`).
|
|
218
|
+
"""
|
|
219
|
+
if self.draft:
|
|
220
|
+
return "draft"
|
|
221
|
+
if not self.product:
|
|
222
|
+
return "nonproduct"
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def board_needs(needs: list[Need], *, include_drafts: bool) -> list[Need]:
|
|
227
|
+
"""The needs on the counted board: a draft (#60) is staged off it until promoted.
|
|
228
|
+
|
|
229
|
+
``include_drafts`` keeps them on (the ``--include-drafts`` flag). The one place the
|
|
230
|
+
draft-staging rule lives, shared by ``status`` and ``plan`` so the two never diverge.
|
|
231
|
+
"""
|
|
232
|
+
return list(needs) if include_drafts else [n for n in needs if not n.draft]
|
|
233
|
+
|
|
197
234
|
|
|
198
235
|
class ArgKind(StrEnum):
|
|
199
236
|
"""A node type in the assurance argument (GSN: Goal Structuring Notation)."""
|
|
@@ -251,7 +251,7 @@ def summarize(results: list[NeedResult]) -> Summary:
|
|
|
251
251
|
denominator too would double-count every flow (#43). Coverage is the leaves; a flow's own
|
|
252
252
|
state is reported separately, never added on top of the children it aggregates.
|
|
253
253
|
"""
|
|
254
|
-
leaves = [r for r in results if
|
|
254
|
+
leaves = [r for r in results if r.need.counts_toward_coverage]
|
|
255
255
|
by_state = {s: sum(1 for r in leaves if r.state is s) for s in NeedState}
|
|
256
256
|
return Summary(
|
|
257
257
|
total=len(leaves),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|