Skip to content

docs: add a development spec; sync the RPM spec with the tree - #10

Open
davdunc wants to merge 5 commits into
mainfrom
chore/spec-sync-0.3.0
Open

docs: add a development spec; sync the RPM spec with the tree#10
davdunc wants to merge 5 commits into
mainfrom
chore/spec-sync-0.3.0

Conversation

@davdunc

@davdunc davdunc commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Why

The repo had no development spec. CONTRIBUTING.md covers environment setup and quality gates, but nothing stated module boundaries, the provider contract, path ownership, or what "shipped" means for a command. Separately, packaging/tradekit.spec had gone stale at 0.1.0 across four merged PRs.

docs/SPEC.md (new)

Records the rules that already hold, and names the ones nothing enforces. The import graph is verified, not aspirational — derived by grepping actual imports:

paths   -> (nothing internal)      config -> paths
data    -> config, paths           analysis -> config
screener-> analysis, config, data  reports -> config, data
agents  -> paths                   cli -> everything

analysis never importing data is what keeps tests/test_analysis.py hermetic — synthetic DataFrames, no network, no keys. That is worth stating as a rule.

Deliberately does not duplicate falcon-suite-compatibility.md (cross-repo contracts C1/C2/C3) or ADR 0001 (why XDG). Scope table at the top says which document owns what.

Seven known gaps recorded rather than hidden

Most notable, and worth a look before merge:

  • G1 — blotter is documented but unreachable. PR feat(reports): round-trip blotter command (tradekit blotter DATE) #2 advertises tradekit blotter DATE and reports/blotter.py ships, but no @cli.command() registers it and --help does not list it. It is not imported anywhere either.
  • G2 — the version disagrees in four places. pyproject.toml 0.2.0 · __init__.py 0.1.0 · tradekit.spec 0.3.0 · CHANGELOG.md 0.2.0. Nothing reads __version__.
  • G3/G4 — the import direction and the spec's Requires:/Commands: lists are documented but unenforced, so they will drift.

G1 and G4 are the same failure twice: a claim published with no check that the claim is true.

packaging/tradekit.spec

  • Version 0.1.00.3.0, %changelog entry added
  • Add Requires: python3-matplotlib >= 3.11.1 — genuinely needed: tradekit.reports.blotter ships and %pyproject_check_import imports it during the build, even though no command exposes it. Comment explains this so it is not "cleaned up" on the strength of G1
  • Correct the Commands: list — news, regime, sandwich, second-day were missing
  • Personal_AI_InfrastructureLifeOS in %description
  • Move completions to %{_datadir}/bash-completion/completions
  • Fix completion generation and %check — both invoked the buildroot entry point with no PYTHONPATH and could never have imported the package
  • Stop masking %check with || :. A smoke test that cannot fail is not a test
  • Drop %config(noreplace) from a .example file; it is documentation, not configuration

Before this can build

⚠️ Version: 0.3.0 has no matching git tag. Source0 resolves to v%{version}, so the release needs tagging — and pyproject.toml and CHANGELOG.md bumping with it — before rpmbuild or COPR will work. Flagged in the spec header.

⚠️ The spec is unvalidated. rpmspec and rpmbuild are not installed on the machine this was written on, so nothing has parsed it. Worth an rpmlint pass before merge.

Test plan

  • ruff check passes
  • Import graph in SPEC.md verified against actual imports
  • Command list verified against tradekit --help (17 commands)
  • tradekit.reports.blotter confirmed importable, justifying the matplotlib dep
  • rpmlint packaging/tradekit.spec
  • rpmbuild -ba on a machine with the tooling

🤖 Generated with Claude Code

https://claude.ai/code/session_01BcNHYKg3TET8SzbFdyZPZb

The repo had no development spec — CONTRIBUTING.md covered environment setup
and quality gates, but nothing stated module boundaries, the provider contract,
path ownership, or what "shipped" means for a command.

docs/SPEC.md records the rules that already hold and names the ones nothing
enforces. The import graph is verified, not aspirational: paths imports nothing
internal, analysis never imports data, nothing imports cli. That second one is
what keeps tests/test_analysis.py hermetic, so it is worth stating.

Six known gaps are listed rather than hidden. Two matter most, and they are the
same failure twice: blotter is advertised by PR #2 and ships as a module but is
not registered as a command, and the RPM spec's Requires/Commands drift from
pyproject and --help with nothing checking either.

packaging/tradekit.spec had gone stale at 0.1.0 across four merged PRs:

- Version 0.1.0 -> 0.3.0
- Add Requires: python3-matplotlib >= 3.11.1 (blotter PNG rendering)
- Correct the command list: add news, regime, sandwich, second-day
- Personal_AI_Infrastructure -> LifeOS in the description
- Move completions to %{_datadir}/bash-completion/completions
- Fix completion generation and %check: both invoked the buildroot entry point
  with no PYTHONPATH and could never have imported the package
- Stop masking %check with `|| :` — a smoke test that cannot fail is not a test
- Drop %config(noreplace) from a .example file; it is documentation

Not validated with rpmspec/rpmbuild — neither is installed here.
Three fixes to the previous commit's work, all found by re-checking claims
against the code rather than against intuition.

The %check comment asserted the CLI smoke test "would have caught an
unregistered command." It would not. `tradekit --help` exits 0 whether or not
a module is wired to a @cli.command(), which is exactly how blotter shipped
unreachable in the first place. Replaced with what the test actually catches
— an import-time error or a missing runtime dep — and a pointer to gap G1.
A spec that documents a check as stronger than it is, is worse than one that
omits it.

Requires: python3-matplotlib now carries its justification. The dependency is
real even though no command exposes blotter: the module ships in the package
and %pyproject_check_import imports it during the build. Without the note the
next person reading the gap list would reasonably delete the Requires and
break %check.

SPEC.md gains an import rule and two corrections:

- New rule 6 — import public names across package boundaries.
  reports/html.py:323 does `from tradekit.data.finviz import
  _trade_review_day_dir` while the public paths.trade_review_day_dir is what
  it wants. Recorded as G7. This is NOT a layering violation, reports may
  import data; it couples a renderer to another package's private helper.
  finviz's own wrapper delegates to paths, so the "nothing outside paths.py
  builds a path" rule does hold.
- G2 was understated: the version disagrees in four places, not three.
  CHANGELOG's top heading is the fourth. Nothing reads __version__ at all.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds comprehensive development documentation (SPEC.md) and syncs the RPM spec to 0.3.0. The documentation is thorough and valuable for the project.

Critical Issue

Version Mismatch Blocks Build: The spec declares Version 0.3.0, but according to SPEC.md gap G2, pyproject.toml is at 0.2.0. This causes Source0 to resolve to a non-existent git tag, making the spec unbuildable. Per the documented release checklist in SPEC.md (lines 206-214), pyproject.toml must be bumped and the tag created before this spec can successfully build.

Recommendation

Following the release checklist documented in this PR: bump pyproject.toml to 0.3.0 and create the v0.3.0 tag before merging, or revert the spec Version to 0.2.0 to match the current state.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread packaging/tradekit.spec

Name: tradekit
Version: 0.1.0
Version: 0.3.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Build Failure: Version 0.3.0 does not match pyproject.toml (0.2.0). Source0 resolves to ` which will fail when the v0.3.0 tag doesn't exist. This makes the spec unbuildable until pyproject.toml is bumped and the tag is created, violating the release checklist requirement that all version locations must agree before tagging.

…down

CI caught this: `ruff format --check .` formats Python inside markdown code
fences, so docs/SPEC.md is subject to the formatter. `ruff check` is not — it
skips markdown entirely, which is why a local `ruff check docs` reported "no
Python files found" and the problem reached CI.

Recorded in SPEC.md's CI gate table so the next person writing a ```python
block in docs does not lose a CI cycle to it.
Found while checking whether this PR's CI failure was self-inflicted. It is
not: the same 3 tests in tests/test_reporting.py fail on origin/main, verified
by checking main out and running them there.

The cause is a half-finished security fix. reporting/ingest.py:34 sets
DEFAULT_ACCOUNT_KINDS = {} with the comment "Load account mappings from
environment or config file instead of hardcoding". Removing hardcoded broker
account ids from a public repo was right. The replacement loader was never
written, so ingest.py:82 falls back to AccountKind.LIVE for every account and
a SIM account reports as LIVE.

That matters more than a red CI badge. LIVE and SIM carry independent risk
units, and telling them apart is the one distinction the whole risk model
rests on. Right now it silently does not hold.

Recorded as G8 rather than fixed here — this PR is documentation, and the fix
is a behaviour change that deserves its own review.
@davdunc

davdunc commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

CI note — the 3 test failures are pre-existing on main, not from this PR

This PR changes two files, both documentation: docs/SPEC.md and packaging/tradekit.spec. No Python.

The ruff format --check failure on the first run was mine and is fixed in a70869aruff format formats Python inside markdown code fences, so docs/*.md is subject to it. ruff check is not, which is why a local ruff check docs reported "no Python files found" and it reached CI. Noted in the SPEC's CI gate table so the next person doesn't lose a cycle to it.

The remaining 3 failures reproduce on main untouched:

git checkout origin/main && pytest tests/test_reporting.py
3 failed, 31 passed

Root cause, recorded as gap G8 in 034ea11. reporting/ingest.py:34:

DEFAULT_ACCOUNT_KINDS: dict[str, AccountKind] = {}
# Load account mappings from environment or config file instead of hardcoding

Removing hardcoded broker account ids from a public repo was the right call. The replacement loader was never written, so ingest.py:82 falls back to AccountKind.LIVE for every account and a SIM account reports as LIVE.

That is worth more attention than a red badge: LIVE and SIM carry independent risk units, and telling them apart is the distinction the risk model rests on. It currently does not hold, silently.

Not fixed here — this PR is docs, and the fix is a behaviour change that deserves its own review.

Amazon Q flagged that the spec declares 0.3.0 while pyproject sits at 0.2.0,
so Source0 resolves to a tag that does not exist and the spec cannot build.
Correct, and it is a fair catch on this PR specifically: SPEC.md's own release
checklist says bump CHANGELOG, then pyproject, then the spec. The previous
commit did step three alone.

- CHANGELOG: [Unreleased] -> [0.3.0], populated from what actually landed after
  the 0.2.0 tag — the canonical reporting layer (#8), the blotter module (#2,
  #7), XDG paths (#4), the PAI -> LifeOS rebrand, and the CVE dependency
  upgrade (#6). The blotter entry says plainly that the module ships without a
  registered command (gap G1) rather than implying a working feature.
- pyproject: 0.2.0 -> 0.3.0.
- __init__: __version__ now derives from installed distribution metadata with a
  dev fallback for an uninstalled source checkout. This deletes the fourth copy
  rather than updating it, which closes G2 structurally instead of resetting the
  clock on the same drift.

The importlib.metadata import sits at the top of __init__ with os and sys, ahead
of the vendor sys.path hook — it is stdlib, so it does not need the hook, and
placing it after would trip E402. Verified the hook still works: finvizfinance
and ta both import.

ruff check and ruff format --check are clean. The 3 test_reporting failures are
unchanged and pre-existing on main (gap G8).

A tag still has to be cut at merge for Source0 to resolve.
@davdunc

davdunc commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

Addressed in 6c9ef0d — version aligned across all four locations

@Amazon-Q-Developer's catch is correct, and it lands squarely on this PR: SPEC.md's own release checklist says bump CHANGELOG.md, then pyproject.toml, then the spec. The earlier commit did step three alone, which is the exact drift the spec was written to document.

Rather than reverting the spec to 0.2.0, I took the other branch of the recommendation and completed the release:

Location Was Now
pyproject.toml 0.2.0 0.3.0
packaging/tradekit.spec 0.3.0 0.3.0
CHANGELOG.md 0.2.0 0.3.0, populated from what landed after the tag
src/tradekit/__init__.py 0.1.0 derived from importlib.metadata

Reverting to 0.2.0 would have left the spec describing code that is not in the 0.2.0 tarball — the matplotlib dependency, four commands added since, and the XDG layout. The forward bump is the coherent option.

__version__ is now derived rather than updated. That deletes the fourth copy instead of resetting the clock on the same drift, which is what closes G2 structurally. It sits at the top of __init__.py with os and sys, ahead of the vendor sys.path hook — stdlib, so it does not need the hook, and placing it after trips E402. Verified finvizfinance and ta still import.

G2 is now marked closed in SPEC.md, with the residual noted: nothing yet asserts the three files stay in agreement, so without the CI check from G4 it will reopen.

Still outstanding

  • A v0.3.0 tag must be cut at merge or Source0 will not resolve. That is a release action, not a code change.
  • The RPM spec is still unparsed — no rpmspec/rpmbuild on the authoring machine. An rpmlint pass before merge would be worth it.
  • CI stays red on the 3 test_reporting.py failures, which are pre-existing on main and recorded as gap G8 — account kind classification falls back to LIVE for every account. Unrelated to this PR and not fixed here.

ruff check and ruff format --check are clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant