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
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

jobs:
status-is-current:
name: STATUS.md matches the code
name: status.md matches the code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
12 changes: 6 additions & 6 deletions .xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! itself, because a promise nothing checks is a promise that decays:
//!
//! * `verify-no-mocks` — no test double is reachable from a shipping binary.
//! * `verify-status` — every `NotImplemented` is declared in `docs/STATUS.md`.
//! * `verify-status` — every `NotImplemented` is declared in `docs/status.md`.

// This is a CLI; stdout is its output medium, not stray debugging.
#![allow(clippy::print_stdout)]
Expand Down Expand Up @@ -117,12 +117,12 @@ fn runtime_dependency_section(manifest: &str) -> String {
out
}

/// Fail if the code claims something is unbuilt that `docs/STATUS.md` does not
/// Fail if the code claims something is unbuilt that `docs/status.md` does not
/// declare — or vice versa. Keeps the status page honest by construction.
fn verify_status(root: &Path) -> Result<(), String> {
let status_path = root.join("docs/STATUS.md");
let status_path = root.join("docs/status.md");
let status = fs::read_to_string(&status_path)
.map_err(|e| format!("docs/STATUS.md: {e} (the status page is mandatory)"))?;
.map_err(|e| format!("docs/status.md: {e} (the status page is mandatory)"))?;

let mut found = BTreeSet::new();
for src in rust_sources(&root.join("backends")) {
Expand All @@ -137,7 +137,7 @@ fn verify_status(root: &Path) -> Result<(), String> {
let undeclared: Vec<_> = found.iter().filter(|t| !status.contains(*t)).collect();
if !undeclared.is_empty() {
return Err(format!(
"these unimplemented items are missing from docs/STATUS.md:\n - {}",
"these unimplemented items are missing from docs/status.md:\n - {}",
undeclared
.iter()
.map(|s| s.as_str())
Expand All @@ -147,7 +147,7 @@ fn verify_status(root: &Path) -> Result<(), String> {
}

println!(
"verify-status: ok — {} unimplemented item(s), all declared in docs/STATUS.md",
"verify-status: ok — {} unimplemented item(s), all declared in docs/status.md",
found.len()
);
Ok(())
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ structurally possible.

- Unwritten code returns `ProviderError::NotImplemented("<crate>::<fn>")`. It
**never** returns a plausible-looking success, an empty list, or a zero.
- Every such token must appear in `docs/STATUS.md`. `cargo xtask verify-status`
- Every such token must appear in `docs/status.md`. `cargo xtask verify-status`
fails the build otherwise — and it fails in both directions.
- Tests for unbuilt behaviour are `#[ignore = "not implemented: … — see
docs/STATUS.md"]`, so a green run never overstates coverage.
- When you finish something, update `docs/STATUS.md` in the same commit. A
docs/status.md"]`, so a green run never overstates coverage.
- When you finish something, update `docs/status.md` in the same commit. A
status page that lags is worse than none, because people trust it.

If you are unsure whether something counts as done: would a test fail if it
Expand Down Expand Up @@ -122,7 +122,7 @@ Each ends with a **Status** section stating what is actually built.
## Commits and PRs

- Conventional commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`).
- A PR that changes behaviour updates `docs/STATUS.md` and the relevant flow doc
- A PR that changes behaviour updates `docs/status.md` and the relevant flow doc
in the same PR.
- `just ci` must pass locally before review.

Expand Down
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ is specific to working here as an agent.

```bash
just verify # the two self-checks; both must pass before AND after your change
cat docs/STATUS.md
cat docs/status.md
```

`docs/STATUS.md` tells you what is actually built. Do not infer capability from
`docs/status.md` tells you what is actually built. Do not infer capability from
the presence of a file — most of this repo is scaffold, and it says so.

## The failure mode to avoid
Expand All @@ -24,20 +24,20 @@ than it is:
- writing a test that asserts nothing so a suite goes green,
- adding a mock adapter to make local development easier,
- rendering fake rows in the dashboard so a screenshot looks good,
- marking something ✅ in `docs/STATUS.md` because it compiles.
- marking something ✅ in `docs/status.md` because it compiles.

Each of these is worse than leaving the gap visible. This is a payment system;
someone will eventually trust it with real money on the strength of what the
repo claims about itself.

If you cannot implement something properly, leave
`ProviderError::NotImplemented`, list it in `docs/STATUS.md`, and say so plainly
`ProviderError::NotImplemented`, list it in `docs/status.md`, and say so plainly
in your summary.

## When you finish a task

1. `just ci`
2. Update `docs/STATUS.md` — in the same commit, not a follow-up.
2. Update `docs/status.md` — in the same commit, not a follow-up.
3. Update the relevant `docs/flows/*.md` **Status** section.
4. In your summary to the user, state explicitly what you did **not** do.

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ MTN MoMo and Orange Money are the first two adapters. Neither is the architectur
> This repository is a **scaffold**. It compiles, lints clean and its tests pass,
> but **no HTTP call to any payment rail has ever been made by this code**.
>
> Read [`docs/STATUS.md`](docs/STATUS.md) before forming any expectation of what
> Read [`docs/status.md`](docs/status.md) before forming any expectation of what
> works. That page is machine-checked: `cargo xtask verify-status` fails the
> build if the code contains an unimplemented path that the status page does not
> declare.
Expand Down Expand Up @@ -53,7 +53,7 @@ rail. `cargo xtask verify-no-mocks` enforces it.

**2. Never claim a feature is done when it is not.** Unwritten code returns
`ProviderError::NotImplemented` — it never fabricates a success. Every such path
must appear in `docs/STATUS.md`, and `cargo xtask verify-status` fails the build
must appear in `docs/status.md`, and `cargo xtask verify-status` fails the build
otherwise. Tests for unbuilt features are `#[ignore]`d with a reason, so a green
run never overstates coverage.

Expand All @@ -69,8 +69,8 @@ frontends/
apps/ dashboard (Next.js)
tests/ e2e (Cypress)
examples/ merchant-curl · merchant-node · webhook-receiver
docs/ adr/ · rfc/ · flows/ · runbooks/ · api/ · STATUS.md
schemas/ *.cstack (syntax verified, design sketch, excluded from the build — see docs/STATUS.md)
docs/ adr/ · rfc/ · flows/ · runbooks/ · api/ · status.md
schemas/ *.cstack (syntax verified, design sketch, excluded from the build — see docs/status.md)
.xtask/ repo automation and the two self-checks
```

Expand Down Expand Up @@ -122,7 +122,7 @@ Neither binary calls a payment rail. `vpay-server` writes rows and serves only
its job loop is not implemented, and it says so in a startup banner and a
repeating heartbeat log line. `--database-url`, `--config` and
`--public-base-url` are accepted but not yet consumed by anything — see
[`docs/STATUS.md`](docs/STATUS.md) and
[`docs/status.md`](docs/status.md) and
[`docs/flows/configuration.md`](docs/flows/configuration.md).

### Known environment gotchas
Expand All @@ -139,11 +139,11 @@ repeating heartbeat log line. `--database-url`, `--config` and
`just build-dist`. `backends/Dockerfile` now builds the host's *implicit*
musl target rather than hardcoding the x86_64 triple, but the Dockerfiles
themselves have not been built in this repo's own development environment —
see [`docs/STATUS.md`](docs/STATUS.md)'s Infrastructure section for why.
see [`docs/status.md`](docs/status.md)'s Infrastructure section for why.

## Documentation

Start with [`docs/STATUS.md`](docs/STATUS.md), then:
Start with [`docs/status.md`](docs/status.md), then:

- [Flows](docs/flows/) — one document per process, with invariants
- [ADRs](docs/adr/) — decisions and what they cost
Expand Down
2 changes: 1 addition & 1 deletion backends/apps/vpay-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn main() -> anyhow::Result<()> {
.await
.with_context(|| format!("binding {}", args.bind))?;

tracing::warn!("vpay-server is a scaffold: only /healthz is implemented. See docs/STATUS.md");
tracing::warn!("vpay-server is a scaffold: only /healthz is implemented. See docs/status.md");
tracing::info!(addr = %args.bind, "listening");

let shutdown_grace = Duration::from_secs(args.common.shutdown_grace_seconds);
Expand Down
10 changes: 5 additions & 5 deletions backends/apps/vpay-worker-bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! vpay worker: submit, poll, reconcile, deliver.
//!
//! The job loop itself is not implemented (`docs/STATUS.md`) — this process
//! The job loop itself is not implemented (`docs/status.md`) — this process
//! supervises nothing yet. It used to exit immediately on start, on the
//! theory that idling in a loop that did nothing would look like a running
//! worker in `docker compose ps`. In practice that made the compose stack
Expand Down Expand Up @@ -35,14 +35,14 @@ async fn main() -> anyhow::Result<()> {
tracing::warn!(
"vpay-worker-bin is a scaffold: the job loop is NOT implemented. No jobs \
are being dequeued, polled, or delivered. This process stays up only to \
answer shutdown signals correctly. See docs/STATUS.md."
answer shutdown signals correctly. See docs/status.md."
);

// `--shutdown-grace-seconds` / `VPAY_SHUTDOWN_GRACE_SECONDS` is parsed
// and validated here (it is shared with `vpay-server` via
// `CommonArgs`), but intentionally not consumed below: there is no
// in-flight work for it to bound yet, because the job loop it is meant
// to bound the drain of does not exist (docs/STATUS.md). Once that loop
// to bound the drain of does not exist (docs/status.md). Once that loop
// is implemented, this value should bound how long it waits for an
// in-flight job to finish after a shutdown signal, the same way
// `vpay-server`'s `main.rs` bounds its HTTP request drain. Wiring it in
Expand All @@ -52,7 +52,7 @@ async fn main() -> anyhow::Result<()> {
tracing::debug!(
shutdown_grace_seconds = args.common.shutdown_grace_seconds,
"shutdown grace period accepted for CLI parity with vpay-server; has no effect yet, \
there is no job loop for it to bound (see docs/STATUS.md)"
there is no job loop for it to bound (see docs/status.md)"
);

let mut heartbeat = tokio::time::interval(HEARTBEAT_INTERVAL);
Expand All @@ -72,7 +72,7 @@ async fn main() -> anyhow::Result<()> {
_ = heartbeat.tick() => {
tracing::warn!(
"vpay-worker-bin heartbeat: job loop still not implemented, no jobs \
are being processed. See docs/STATUS.md."
are being processed. See docs/status.md."
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-adapter-mtn-momo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! STATUS: capabilities are real and enforced; wire calls are NOT implemented.
//! Every unimplemented method returns `ProviderError::NotImplemented` — it never
//! fabricates a success. See `docs/STATUS.md` and `docs/flows/adapter-mtn-momo.md`.
//! fabricates a success. See `docs/status.md` and `docs/flows/adapter-mtn-momo.md`.

use vpay_core::{Money, ProviderFlow};
use vpay_provider::{
Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-adapter-orange-money/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! STATUS: capabilities are real and enforced; wire calls are NOT implemented.
//! Every unimplemented method returns `ProviderError::NotImplemented` — it never
//! fabricates a success. See `docs/STATUS.md` and `docs/flows/adapter-orange-money.md`.
//! fabricates a success. See `docs/status.md` and `docs/flows/adapter-orange-money.md`.

use vpay_core::{Money, ProviderFlow};
use vpay_provider::{
Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! The Stripe-shaped HTTP surface.
//!
//! STATUS: only `/healthz` and the Stripe-shaped 404 envelope are implemented.
//! No `/v1/*` route exists yet. See `docs/STATUS.md` — this file must never
//! No `/v1/*` route exists yet. See `docs/status.md` — this file must never
//! grow a route that returns fabricated data.

use axum::{Json, Router, http::StatusCode, routing::get};
Expand Down
10 changes: 5 additions & 5 deletions backends/crates/vpay-config/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct CommonArgs {
/// Postgres connection string.
///
/// Accepted and threaded through even though the database layer is not
/// implemented yet (`docs/STATUS.md`) — the wiring exists so turning the
/// implemented yet (`docs/status.md`) — the wiring exists so turning the
/// layer on later is a matter of using this value, not inventing a new
/// flag.
#[arg(long, env = "DATABASE_URL")]
Expand All @@ -59,7 +59,7 @@ pub struct CommonArgs {
/// Path to the YAML configuration file (ADR-0003).
///
/// Optional because figment layering / file loading is not implemented
/// yet (`docs/STATUS.md`).
/// yet (`docs/status.md`).
#[arg(long, env = "VPAY_CONFIG")]
pub config: Option<PathBuf>,

Expand All @@ -79,7 +79,7 @@ pub struct CommonArgs {
/// before the process stops waiting and exits (see `main.rs`).
/// `vpay-worker-bin` accepts and validates this same flag for parity
/// across binaries, but has no in-flight work to bound yet — the job
/// loop is not implemented (`docs/STATUS.md`) — so today it has no
/// loop is not implemented (`docs/status.md`) — so today it has no
/// effect there.
#[arg(long, env = "VPAY_SHUTDOWN_GRACE_SECONDS", default_value_t = 25)]
pub shutdown_grace_seconds: u64,
Expand All @@ -93,7 +93,7 @@ pub struct CommonArgs {
about = "vpay payment gateway API server",
long_about = "vpay payment gateway API server.\n\nWrites rows and returns; it never calls a payment rail itself \
(see docs/flows). This binary is a scaffold — run with --help \
to see the full flag set, and see docs/STATUS.md for what is \
to see the full flag set, and see docs/status.md for what is \
actually implemented behind it."
)]
pub struct ServerArgs {
Expand All @@ -119,7 +119,7 @@ pub struct ServerArgs {
version,
about = "vpay background worker",
long_about = "vpay background worker: submit, poll, reconcile, deliver.\n\nThe job \
loop is not implemented yet (docs/STATUS.md) — this process stays up \
loop is not implemented yet (docs/status.md) — this process stays up \
and answers shutdown signals so orchestration (docker compose, k8s) \
behaves correctly around it, but it processes no jobs."
)]
Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! never select a *code path*. See `docs/adr/0003-yaml-configuration.md`.
//!
//! STATUS: types and the deployment guard rules are implemented and tested.
//! Figment layering and DB reconciliation are NOT implemented — `docs/STATUS.md`.
//! Figment layering and DB reconciliation are NOT implemented — `docs/status.md`.

use serde::{Deserialize, Serialize};

Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-ledger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! merchant received. See `docs/flows/ledger.md`.
//!
//! STATUS: types and the balancing invariant are implemented and tested.
//! Persistence is NOT implemented — see `docs/STATUS.md`.
//! Persistence is NOT implemented — see `docs/status.md`.

use vpay_core::{Money, MoneyError};

Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub enum ProviderError {
#[error("operation not supported by this rail")]
Unsupported,
/// Not yet built. This is NOT a mock: it never pretends to succeed.
/// Every occurrence must appear in `docs/STATUS.md`.
/// Every occurrence must appear in `docs/status.md`.
#[error("not implemented yet: {0}")]
NotImplemented(&'static str),
}
Expand Down
2 changes: 1 addition & 1 deletion backends/crates/vpay-worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Everything that touches the network happens here, never in the API process.
//!
//! STATUS: only the poll ladder is implemented and tested. Job dequeue,
//! submission and delivery are NOT implemented — see `docs/STATUS.md`.
//! submission and delivery are NOT implemented — see `docs/status.md`.

use std::time::Duration;

Expand Down
8 changes: 4 additions & 4 deletions backends/tests/conformance/tests/adapter_conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//!
//! STATUS: only capability-level cases run today. The wire-level cases are
//! `#[ignore]`d with a reason until the adapters exist, so a green run never
//! overstates coverage. See docs/STATUS.md.
//! overstates coverage. See docs/status.md.

use vpay_provider::{Capabilities, ProviderAdapter, ProviderError};

Expand Down Expand Up @@ -66,19 +66,19 @@ fn unimplemented_operations_never_fabricate_success() {
// ---------------------------------------------------------------------------

#[test]
#[ignore = "not implemented: submit() — see docs/STATUS.md"]
#[ignore = "not implemented: submit() — see docs/status.md"]
fn duplicate_submit_reports_submitted_not_an_error() {
unreachable!("enable when submit() lands")
}

#[test]
#[ignore = "not implemented: query_status() — see docs/STATUS.md"]
#[ignore = "not implemented: query_status() — see docs/status.md"]
fn not_found_is_never_on_its_own_a_failure() {
unreachable!("enable when query_status() lands")
}

#[test]
#[ignore = "not implemented: redirect flow — see docs/STATUS.md"]
#[ignore = "not implemented: redirect flow — see docs/status.md"]
fn redirect_rails_commit_ref_extra_before_returning_a_url() {
unreachable!("enable when the Orange adapter lands")
}
4 changes: 2 additions & 2 deletions backends/tests/integration/tests/postgres_smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
//! and is `#[ignore]`d so it cannot report false confidence.

#[test]
#[ignore = "not implemented: testcontainers harness — see docs/STATUS.md"]
#[ignore = "not implemented: testcontainers harness — see docs/status.md"]
fn schema_migrates_cleanly_on_an_empty_database() {
unreachable!("enable when migrations land")
}

#[test]
#[ignore = "not implemented: testcontainers harness — see docs/STATUS.md"]
#[ignore = "not implemented: testcontainers harness — see docs/status.md"]
fn one_charge_per_intent_is_enforced_by_the_database() {
unreachable!("enable when migrations land")
}
2 changes: 1 addition & 1 deletion compose.e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ services:
# healthcheck to report healthy (see the comment above it). This is a
# boot-order guarantee only — the dashboard can still start before the
# server has finished binding its listener. That race is currently
# harmless: per docs/STATUS.md the dashboard renders a static scaffold
# harmless: per docs/status.md the dashboard renders a static scaffold
# notice and makes no server-side call to `vpay-server` yet. Revisit
# this once the dashboard actually calls `/dash/v1` on render.
vpay-server: { condition: service_started }
Expand Down
2 changes: 1 addition & 1 deletion docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Authenticated with `sk_live_` / `sk_test_` keys. Form-encoded bodies, Stripe's
object model, Stripe's error envelope, Stripe's idempotency semantics.

**Status: not implemented.** Only `/healthz` and a Stripe-shaped 404 exist. See
[../STATUS.md](../STATUS.md).
[../status.md](../status.md).

Planned subset:

Expand Down
2 changes: 1 addition & 1 deletion docs/flows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ go wrong, and what invariant holds throughout.
| [adapter-mtn-momo.md](adapter-mtn-momo.md) | MTN specifics — push flow |
| [adapter-orange-money.md](adapter-orange-money.md) | Orange specifics — redirect flow |

Every flow here is *designed*. See [../STATUS.md](../STATUS.md) for which parts
Every flow here is *designed*. See [../status.md](../status.md) for which parts
are actually built.
Loading
Loading