Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
All notable changes to the Goldpath packages are documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning: SemVer.

## [Unreleased]

### Added
- **Goldpath.Approvals** (new Ring B module, core landed 2026-08-18): human approval
workflows — amount-laddered authority chains declared as data, four-eyes/maker-checker
enforcement, bounded delegation (depth one), deadline escalation with top-rung expiry,
the worklist, and the full audit trail; lifecycle published as integration events.
17 deterministic tests; `features.approvals` + `goldpath add feature approvals`.
- **Goldpath.FileExchange** (new Ring B module, core landed 2026-08-18): file-based
integration rails as a unit — rails declared as data with baked closures, file-level
contracts, idempotent `(file, line)` ingestion, per-row quarantine that never stops the
batch, zero-duplicate replay/reprocess, archive marks; lifecycle published as
integration events. 7-test planted-fault rig; `features.fileExchange` +
`goldpath add feature fileexchange`.

Both cores ship with in-memory stores behind seams (`IGoldpathApprovalStore`,
`IGoldpathFileLedger`); database-backed stores, template flags and console federation
are tracked in the RFCs' open DoD rows (T21/T22).

## [0.1.0-preview.6] - 2026-08-03

The operations train. Campaign revision R1 gives long-running campaigns their calendar
Expand Down
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
<PackageVersion Include="MailKit" Version="4.17.0" />
<PackageVersion Include="SSH.NET" Version="2026.0.0" />
<PackageVersion Include="Mediant" Version="1.4.0" />
<PackageVersion Include="Mediant.AspNetCore" Version="1.4.0" />
<PackageVersion Include="Mediant.Behaviors" Version="1.4.0" />
Expand All @@ -22,6 +23,7 @@
<PackageVersion Include="Microsoft.Extensions.Compliance.Redaction" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.Telemetry" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.9" />
<PackageVersion Include="Testcontainers.Redis" Version="4.13.0" />
Expand Down
60 changes: 60 additions & 0 deletions Goldpath.sln
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.Console.Tests", "t
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.Sdk", "packages\Goldpath.Sdk\Goldpath.Sdk.csproj", "{58BE5B0A-D181-48EA-974F-8D924A553186}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.Approvals", "packages\Goldpath.Approvals\Goldpath.Approvals.csproj", "{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.Approvals.Tests", "tests\Goldpath.Approvals.Tests\Goldpath.Approvals.Tests.csproj", "{163EB0DB-9785-4C80-815F-863670023278}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.FileExchange", "packages\Goldpath.FileExchange\Goldpath.FileExchange.csproj", "{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Goldpath.FileExchange.Tests", "tests\Goldpath.FileExchange.Tests\Goldpath.FileExchange.Tests.csproj", "{49780B31-D85B-437B-BDE1-6A47769D7C66}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -681,6 +689,54 @@ Global
{58BE5B0A-D181-48EA-974F-8D924A553186}.Release|x64.Build.0 = Release|Any CPU
{58BE5B0A-D181-48EA-974F-8D924A553186}.Release|x86.ActiveCfg = Release|Any CPU
{58BE5B0A-D181-48EA-974F-8D924A553186}.Release|x86.Build.0 = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|x64.ActiveCfg = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|x64.Build.0 = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|x86.ActiveCfg = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Debug|x86.Build.0 = Debug|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|Any CPU.Build.0 = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|x64.ActiveCfg = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|x64.Build.0 = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|x86.ActiveCfg = Release|Any CPU
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE}.Release|x86.Build.0 = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|Any CPU.Build.0 = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|x64.ActiveCfg = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|x64.Build.0 = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|x86.ActiveCfg = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Debug|x86.Build.0 = Debug|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|Any CPU.ActiveCfg = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|Any CPU.Build.0 = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|x64.ActiveCfg = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|x64.Build.0 = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|x86.ActiveCfg = Release|Any CPU
{163EB0DB-9785-4C80-815F-863670023278}.Release|x86.Build.0 = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|x64.ActiveCfg = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|x64.Build.0 = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|x86.ActiveCfg = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Debug|x86.Build.0 = Debug|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|Any CPU.Build.0 = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|x64.ActiveCfg = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|x64.Build.0 = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|x86.ActiveCfg = Release|Any CPU
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311}.Release|x86.Build.0 = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|x64.ActiveCfg = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|x64.Build.0 = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|x86.ActiveCfg = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Debug|x86.Build.0 = Debug|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|Any CPU.Build.0 = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|x64.ActiveCfg = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|x64.Build.0 = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|x86.ActiveCfg = Release|Any CPU
{49780B31-D85B-437B-BDE1-6A47769D7C66}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -733,5 +789,9 @@ Global
{1616453C-824E-42DB-8FB1-63E824736568} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE}
{C44229F0-A168-466E-8E29-EC6321827C59} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{58BE5B0A-D181-48EA-974F-8D924A553186} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE}
{0A6CFB92-354E-4C57-AAB8-3F52F9646AEE} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE}
{163EB0DB-9785-4C80-815F-863670023278} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{4F6B1244-4268-4E2F-BCB5-58A3C8B93311} = {809F86A1-1C4C-B159-0CD4-DF9D33D876CE}
{49780B31-D85B-437B-BDE1-6A47769D7C66} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
4 changes: 3 additions & 1 deletion docs/rfc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ All "implemented" modules are published on nuget.org at `0.1.0-preview.6` (the c
| [goldpath-messaging-exit](goldpath-messaging-exit.md) | the messaging dependency: measured exposure, options, and the triggers that would move us | **ACCEPTED** (2026-08-10) — option **A**: stay pinned on 8.x, fallback D; a move would be a MAJOR version, not an internal swap. §5 implemented (the publish seam + GP0404); the watch lives as `open-threads.md` T18 |
| [spec-engine-v1](spec-engine-v1.md) | specdrift (separate repo) | implemented — 0.4.2 published (NuGet tool + MCP + Docker + Action) |
| [qorpe-ui](qorpe-ui.md) | @qorpe/ui — the family UI kit extraction (platform-sdk D5) | **implemented** (step 6.0, 2026-08-07): the kit lives in [qorpe/ui](https://github.com/qorpe/ui), publishes to npm via OIDC trusted publishing, and BOTH consumers run on the published package — `ui/kit` is deleted from this repo |
| [qorpe-sync](qorpe-sync.md) | qorpe.sync (separate private repo) — the migration & co-existence product module (ADR-0012's own named example): CDC capture, ordered replayable stream, adapter-based versioned mapping, event-id reconciliation composing db-compare | **proposed** — owner acceptance pending; v0 scope is engagement-shaped (§5.1 by-product rule); opens the second product repo beside the API Portal pilot, recorded as an owner ordering decision (RFC D5) |
| [qorpe-sync](qorpe-sync.md) | qorpe.sync (separate private repo) — the migration & co-existence product module (ADR-0012's own named example): CDC capture, ordered replayable stream, adapter-based versioned mapping, event-id reconciliation composing db-compare | **accepted** (owner, 2026-08-18) — v0 scope is engagement-shaped (§5.1 by-product rule); build scheduled, two-store rig first; the second-product-repo ordering is an owner decision recorded in RFC D5 |
| [goldpath-approvals](goldpath-approvals.md) | Goldpath.Approvals — human approval workflows: maker-checker, four-eyes, amount-laddered authority, delegation, escalation, worklist; definitions as versioned data | **accepted** (owner, 2026-08-18) — build started same day by owner decision; core + 17 tests + manifest/CLI wiring landed; console federation, DB store and the adopter proof stay open (T21) |
| [goldpath-fileexchange](goldpath-fileexchange.md) | Goldpath.FileExchange — file-based integration rails as a unit: scheduled pick-up/drop, format contracts, idempotent `(file,line)` ingestion, quarantine, reprocessing, archival, outbound | **accepted** (owner, 2026-08-18) — build started same day by owner decision; core + 7-test planted-fault rig + manifest/CLI wiring landed; transports, console federation, DB ledger and the adopter proof stay open (T22) |
| [specanchor-composition](specanchor-composition.md) | specanchor (separate repo) — the transformation package's deterministic core, composed per foundation §9/§5.1 | **ACCEPTED** (2026-08-16, #165) — the toolchain is real ([qorpe/specanchor](https://github.com/qorpe/specanchor), Apache-2.0; 74 tests, mutation 94.8%, first skill eval 7/7); the composition proof it owes is `open-threads.md` T20 (the rehearsal) |
106 changes: 106 additions & 0 deletions docs/rfc/goldpath-approvals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# RFC: Goldpath.Approvals — Human Approval Workflows

**Status:** accepted (owner, 2026-08-18) — the owner pulled the Ring B trigger the same day ("nothing left incomplete"); recorded as an explicit owner ordering decision, mirroring qorpe-sync D5
**Date:** 2026-08-18
**Constitution grounding:** ADR-0003 (compose, don't rewrite — this module orchestrates PEOPLE'S
decisions, not systems; process orchestration remains the decided non-goal the closed T19 thread
proved composable), foundation §5.1 (by-product timing rule), §6.2 Ring B criteria,
open-threads **T21** (this RFC is that thread's first DoD row).

---

## 1. Scope / Non-Goals

**Scope.** The approval mechanics that today live in e-mail threads at every enterprise adopter,
as one composable Ring B module:

- **Approval definitions as data** — maker-checker, four-eyes, and amount-laddered authority
chains (e.g. expert → deputy → manager → GM) declared as schema-validated artifacts, never
code. A new ladder is a new definition, not a fork.
- **Decision lifecycle** — request → pending → granted/rejected/expired, with every transition
audited; a guarded action proceeds only on a granted decision.
- **Delegation and escalation** — bounded delegation (no cycles, windowed), deadline-driven
escalation up the ladder.
- **Worklist** — the pending-approvals inbox surface (per-approver, per-ladder) that feeds
operator UIs and the family console.

**Non-goals.** Not a BPM/workflow engine (no arbitrary process graphs); not process
orchestration across services (that composes as a state machine per the T19 proof — this module
is what such a flow CALLS when a step needs a human); not an identity/org-chart system —
authority levels map onto the app's existing Auth roles/claims.

## 2. Seam Map

- **Auth** — who MAY decide at each rung; authority mapping is claims-based.
- **AuditTrail** — every request, decision, delegation, and escalation is an audited event;
the module's value proposition IS the audit trail e-mail never had.
- **Notification** — pending/escalation nudges replace the e-mail thread, not augment it.
- **Jobs** — escalation and expiry timers (Quartz-backed, no bespoke scheduler).
- **Messaging** — `ApprovalGranted`/`ApprovalRejected`/`ApprovalExpired` as
`IIntegrationEvent`s; GP0401–0403 hold the boundary unchanged.

## 3. Manifest Surface

`features.approvals` — enabled or absent (compile-time composition; a manifest without it has
NO approvals code). Ladder definitions live beside the manifest as versioned declarative
artifacts validated at build by the standard schema gate.

## 4. API Surface

Admin-contract (R3) shaped, so the family console federates it without bespoke work:
worklist (repeatable OR filters), decide, delegate, history per subject; all responses typed
in OpenAPI. The application-facing surface is one interface: request an approval for a subject
under a ladder, and observe/await its outcome.

## 5. Analyzer Rules

- A guarded operation (one declaring it requires approval) reachable without an approval-gate
check fails the build — the same "the standard ships its verifier" rule every module obeys.
- Approval events not marked `IIntegrationEvent` are already caught by GP0401.

## 6. Ops Package ("no runbook = no module")

Runbook + dashboard: pending-decision age (the number the e-mail world cannot produce),
escalations fired, per-ladder throughput, expiry rate. Alarm on oldest-pending breaching the
ladder's own deadline.

## 7. Test Plan

- Ladder boundary values (the adopter PoC's amount ladder is the seed shape: each rung's
inclusive edge, above-top-rung routing).
- Delegation cycle guard and window expiry; escalation timers on a virtual clock.
- Audit completeness: every lifecycle transition appears exactly once in the trail.
- Adopter proof (the T21 proof column): one REAL amount-laddered flow from the first adopter
runs end to end on the module, audit trail inspected.

## 8. DoD

- [x] RFC accepted; the four Ring B entry criteria confirmed (≥2 industries named:
banking-class approvals, insurance underwriting sign-off, telco credit overrides).
- [x] Ladders declared as data through the fluent surface; declaration-time validation
(no top rung / non-increasing ceilings) rejects malformed chains at composition.
(A standalone YAML schema for config-file ladders is a follow-on, not shipped.)
- [x] Lifecycle + delegation + escalation proven by the §7 deterministic tests
(17 tests: rung boundaries inclusive, four-eyes, wrong-role refusal, double-decide,
escalation resets the rung clock, top-rung expiry, delegation depth-one + expiry,
worklist ordering, trail completeness — `tests/Goldpath.Approvals.Tests`).
- [x] `features.approvals` manifest key + `goldpath add feature approvals` CLI recipe wired
(template flag lands with the module's template pass).
- [ ] Admin surface federates in the family console against a real app.
- [x] Runbook ships (`packages/Goldpath.Approvals/ops/approvals.md`); dashboard JSON open.
- [ ] Database-backed `IGoldpathApprovalStore` (the in-memory store is single-node).
- [ ] The adopter proof runs (§7 last row) — the row that actually closes T21.

### Decisions

- **D1 — Ring B, born as a by-product (§5.1):** built when the first adopter's implementation
phase needs its first systematized approval flow — this RFC prepares the shelf; it does not
open a front. Six of the factoring-class engagement's twelve common processes run approvals
over e-mail today; the ladder shape is domain-agnostic.
- **D2 — Definitions are data:** ladders, quorums, deadlines, delegation windows are versioned
declarative artifacts. Code changes are module changes; ladder changes are config reviews.
- **D3 — The human/saga boundary:** a compensating flow (T19) orchestrates SYSTEMS and may
request an approval as one of its steps; Approvals never drives system steps itself. One
sentence each side, so neither module grows into the other.
- **D4 — Worklist is part of the module,** not left to each adopter's UI team: without the
inbox, adopters fall back to e-mail and the audit value evaporates.
Loading
Loading