Commit graph

9 commits

Author SHA1 Message Date
9566e16a97 Implement Development Effort Calculator (WP-0010-T02)
src/target_revenue/effort_calculator.py implements Candidate A
(labor-cost-anchored, accepted T01): commit-timestamp session-gap
clustering for human interaction time, workplan/task-volume counts via
direct workplans/ directory parsing (no state-hub dependency, works
uniformly on any repo using this repo's own convention), file/line
counts with generated/vendored-path exclusion, and caller-supplied
token-cost pricing. estimate_target_basis() combines these and returns
a derivation dict (every input shown) plus a warnings list - never a
black-box dollar figure.

1-day manual-work floor, as requested: any raw commit-clustered
estimate below 1.0 day is floored and flagged with a warning that this
is very likely a measurement gap (commit-clustering is a floor
estimate by design) that should usually be compensated for by manual
override, not trusted at face value. A second, independent
sanity-check warning fires when finished-workplan/task volume is
substantial but the time estimate is still low, even above the floor -
demonstrated live against target-revenue's own history (7 finished
workplans, 57 tasks correctly flagged a 2.38-day estimate as
under-counted).

scripts/effort_calculator_cli.py: CLI wrapper printing JSON, following
the same offline-first, no-Phase-declaration pattern as
scripts/trf_onboard.py. tests/test_effort_calculator.py (15
deterministic tests, throwaway git repos/tmp_path fixtures) covers
commit clustering, workplan/task parsing, size-metric exclusion,
token-cost pricing, the floor-and-warning behavior, the sanity-check
warning, and an end-to-end smoke test. No new hard dependency.
2026-07-30 13:16:44 +02:00
b7b985d96d Implement hosted Breach/Compliance Record publication (WP-0006-T09)
migrations/0004_breach_records.sql models a case's lifecycle as
append-only events (alleged/cured/determined/terminated) grouped by
case_id rather than one mutable row - resolution is always a new,
later event, never an edit. A CHECK constraint makes the
anonymized-default rule (License V1C1 §7.4) a database fact:
named_entitlement_holder can be set if and only if anonymized = false.

src/target_revenue/breach_record.py's publish_breach_event() enforces
per-Licensor phase ownership and rejects named-disclosure requests
that don't also set named_disclosure_authorized_under_cua: true - the
Trust Service records the Licensor's assertion that the CUA's naming
clause authorizes it, it never verifies the underlying CUA text
itself. Signs every event with the same instance Ed25519 key already
used for Ledger entries and Attestations.

Adds POST/GET /phases/{id}/breach-records. Guarded the .registry
import behind a lazy in-function import (matching attestation.py's
TYPE_CHECKING pattern) so tests/test_breach_record.py (7 tests) runs
under plain system Python with no psycopg dependency. 5 new
Docker-gated tests cover the default-anonymized lifecycle, the
named-disclosure authorization gate, cross-Licensor rejection,
signature verification, and DB-level UPDATE/DELETE rejection.

This closes WP-0006 again - all 9 tasks done.
2026-07-29 22:23:41 +02:00
28f0f429b2 Implement hosted Conversion Attestation publication (WP-0006-T06)
migrations/0003_attestations.sql: one row per Phase, no UPDATE/DELETE
grant for trf_app (a published attestation is permanent).

src/target_revenue/attestation.py: publish_attestation() is idempotent
(existing row returned unchanged, never regenerated) and derives
conversion_timestamp from the ledger itself via _find_conversion_prefix(),
which finds the earliest prefix where the fold reaches Outstanding
Target = 0 rather than trusting entries[-1] or wall-clock publish time -
so a later unrelated entry can never change an already-converged Phase's
recorded conversion moment. Raises NotConvertedError rather than
fabricating a conversion. Reuses conversion.generate_attestation()
unchanged. Guarded the psycopg import behind TYPE_CHECKING so the pure
helper stays testable under plain system Python.

service/app.py adds GET /phases/{id}/attestation (unauthenticated,
publish-on-first-observation).

tests/test_attestation.py (3 tests, no Docker/Postgres) proves the
earliest-crossing behavior. 6 new Docker-gated tests in
test_ledger_hosting.py cover pre-conversion 404, the core
legal-technical property that /metrics facts/calculations/forecasts
are identical whether or not /attestation was ever called, one-time
publication, signature verification, and DB-level UPDATE/DELETE
rejection.
2026-07-29 21:50:02 +02:00
dfc1d90c28 Implement public metrics service (WP-0006-T05)
src/target_revenue/metrics.py: compute_metrics(manifest, entries, as_of)
is pure/deterministic like fold.py, reusing fold.py/conversion.py
unchanged. Returns facts/calculations/forecasts as three explicitly
separated blocks (TrustServicePRD TS-FR-5), covering the mandatory Q9
set plus the recommended velocity/forecast tier - forecasts are always
null rather than populated once a Phase has converted or velocity is
non-positive, so nothing disguises a projection as a fact.

Adds GET /phases/{id}/metrics (unauthenticated per FR-9/FR-10).
tests/test_metrics.py (6 tests) needs no Docker/Postgres and runs
under plain system Python. One new Docker-gated test in
test_ledger_hosting.py proves the hosted /metrics response exactly
matches compute_metrics() run offline against the same export.
2026-07-29 21:42:52 +02:00
5064815e77 Implement hosted Target Ledger append API (WP-0006-T04)
migrations/0002_ledger.sql adds ledger_entries with an identity-column
sequence for exact append order and no UPDATE/DELETE grant for trf_app.
src/target_revenue/ledger.py: append_entry() rejects caller-supplied
previous_entry_hash/signature, enforces per-Licensor phase ownership,
serializes concurrent appends via pg_advisory_xact_lock, computes the
chain tip and signs with the Trust Service instance's own Ed25519 key
(service/keys.py), reusing validation.py's checks unchanged. Adds
POST/GET /phases/{id}/ledger and an unauthenticated GET /public-key.

Also fixes a route-ordering bug found while wiring this in: phase IDs
never needed the {phase_id:path} converter (they contain colons, not
slashes), and its greedy matching was swallowing /ledger-suffixed
paths into the plain GET /phases/{id} route.

tests/test_ledger_hosting.py (8 Docker-gated tests) exercises hash-chain
linkage, forged-field rejection, cross-Licensor isolation, currency and
duplicate-id rejection, DB-privilege enforcement, signature
verification via the public-key endpoint, and the task's own
highest-priority property: append -> export -> offline fold reproduces
the exact expected Development/Remission Credit and Outstanding Target.
2026-07-29 21:34:27 +02:00
7e0c62a8b5 Implement hosted Phase/Extension Registry (WP-0006-T03)
Adds migrations/0001_registries.sql (licensors, phase_manifests,
extensions tables; trf_app role with no UPDATE/DELETE grant on either
table, canonicalization only via a SECURITY DEFINER function), and
src/target_revenue/registry.py + service/app.py: a thin FastAPI layer
wrapping the existing validation.py checks with persistence and
per-Licensor token auth, adding no new validation logic per ADR-0002.
New optional service/service-dev dependency groups keep the core
offline library dependency-free. tests/test_registry_hosting.py (7
tests, Docker-gated, auto-skip otherwise) spins an ephemeral disposable
Postgres container and verifies registration, rejection, duplicate/
unknown-token handling, extension canonicalization, and two explicit
database-privilege checks that the app role cannot bypass the
append-only/governance-gated guarantees.
2026-07-29 21:03:52 +02:00
55a1756f7c Merge spec/ into specs/: one specs directory for the whole repo
Moves TargetRevenueLicenseConcept.md from the separate singular spec/
directory into specs/ (git mv, preserving history) and updates every live
cross-reference (README, CONTRIBUTING, all specs/*.md, workplans, schema
comments, source docstrings, test file) to the new path.

This resolves the spec/ vs specs/ split that history/260728-SWOT-Assessment.md
flagged as a "perpetual footgun" and recommended deciding on. The historical
record of that split and the recommendation itself are left unedited in
history/ (a dated assessment, not a living document) — only README and TSD
now document the merge as resolved, with a pointer back to that history file
for context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 10:22:14 +02:00
2b675c11ad Split administrative-correction into typed development/remission corrections
Resolves the WP-0003-T06 review flag: the single administrative-correction
ledger entry type had an unstated fold-side effect (concept §17 names the
type but never says which side of the target it corrects). Maintainer chose
option (a) — split into administrative-correction-development and
administrative-correction-remission so the corrected side is explicit in
the type name rather than an implicit library default.

- schemas/ledger_entry.schema.json: enum split, no other behavior change.
- src/target_revenue/fold.py: each new type maps to its named side only.
- specs/TargetLedgerSpecification.md, TechnicalSpecificationDocument.md
  §3.2, ProductRequirementsDocument.md FR-5: updated to the split types.
- tests/test_ledger_fold.py: dedicated coverage for both new types plus a
  regression test that the old unsplit type name is now rejected.

spec/TargetRevenueLicenseConcept.md §17 is left unedited — its entry-type
list is explicitly non-exhaustive ("may include"), so this specializes
rather than contradicts it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 09:16:22 +02:00
57c7111cbc Implement TREV-WP-0002 Stage 0 foundation: schemas, pure fold, golden Phase
Delivers the offline runnable specification foundation for the Trust Layer
(TSD §3-§6), not a hosted Trust Service:

- JSON Schemas for Phase Manifest, Ledger Entry, Extension Contract, and
  Conversion Attestation, encoding the Stage 0 working defaults (Q3 future
  license enum, Q6 single-currency Phases, Q8 required longstop_at).
- src/target_revenue: pure Outstanding Target fold, SHA-256 hash-chain
  verification, Ed25519 signing helpers, extension conformance checks
  (including a core-term-redefinition heuristic), and conversion detection
  that never requires an attestation document to determine conversion status.
- examples/phase-001: golden Phase package matching the concept doc's
  worked example, generated via scripts/generate_golden_phase.py so the
  hash chain is computed by the library itself, not hand-typed.
- 32 passing pytest tests covering manifest/ledger/extension conformance,
  tamper/reorder detection, and the full lifecycle fold to conversion.
- docs/adr/ADR-0001: proposed (not accepted) Stage 0 stack choice, per the
  WP-0002-T01 human-accept gate — implementation proceeded against the
  proposal as the workplan note permits, but the task stays open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 18:47:29 +02:00