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
31 changes: 18 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
is now built for Node 22+ (tsup's `target` was `node18`). Node 18
reached end of life in April 2025 and Node 20 in April 2026; neither
gets security fixes. CI tests the maintained LTS lines, 22, 24 and 26,
so a consumer on 18 or 20 was already untested. (The slack bot runs 24
and gh-v1 requires 22; agility-rooms runs in the browser.)
so a consumer on 18 or 20 was already untested. (The chat-bot consumer
runs 24 and the CLI consumer requires 22; the browser app consumer
does not run on Node.)

## 1.0.0-alpha.5

### Added

- **Conditional schema reads** (agility-rooms D1/D3). meta.v1 sends one
- **Conditional schema reads** (for a browser app consumer). meta.v1 sends one
strong ETag for the whole schema (`"26.3.1.13.<stamp>"`, the same on
the document, a type and an attribute) and honours `If-None-Match` with
a 304. `client.meta.read({ ifNoneMatch })` and `readType(token, {
Expand All @@ -24,7 +25,7 @@
the smallest resource. `TypeCache.etag` remembers the tag the loaded
types came with, and `client.schema.revalidate()` asks the server in
one small read and drops the loaded types only when the tag moved —
what rooms' metamodel store does by hand. Transport: a request with
what a browser app's metamodel store does by hand. Transport: a request with
`acceptNotModified` resolves to undefined on a 304 after `onResponse`
sees it.

Expand All @@ -34,24 +35,28 @@
defaults to 1, was 0): a GET, a query.v1 read or a guarded batch that
says so, after a network failure or a 5xx, 150 ms later. A plain write
is never resent, as before. Pass `retries: 0` for the old behaviour.
(gh-v1: the doc promised a retry the default never made.) A 429 counts
(a CLI consumer: the doc promised a retry the default never made.) A 429 counts
as transient like a 5xx — the instance throttles per token — and a
Retry-After is honoured when sent, up to 30 s.
- **A rejected query's `details` are the exceptions alone**
(`{ type, message }[]`, exported as `QueryRejection`), not query.v1's
whole error document; `message` is unchanged. (gh-v1: a formatter that
whole error document; `message` is unchanged. (a CLI consumer: a formatter that
printed details printed a JSON document.)
- **`CommandFailureDetail.echoed` is not enumerable**: still readable on
every failure for a corrected resend, but a serialised failure (a
receipt, a log line, a bot's result) carries index, message and
violation only. (slack bot, gh-v1: re-mapping failures to keep results
violation only. (chat-bot and CLI consumers: re-mapping failures to keep results
small.)
- **The committed `metamodel.json` names no instance**: every `href` is
spelled from `/meta.v1/` (`scripts/download-metamodel.mjs` rewrites
them on a pull). The generated types are unchanged; nothing in the SDK
reads an href.

## 1.0.0-alpha.4 — what the three adoptions asked for

### Added

- **Two narrow rest-1 paths** (agility-rooms): `client.data.operate(oid,
- **Two narrow rest-1 paths** (for a browser app consumer): `client.data.operate(oid,
op)` runs an operation for its result — api/asset performs a Copy but
reports only the source; rest-1 answers the copy's OID — and
`client.copy(oid)` is that for Copy. `client.data.template(type, {
Expand All @@ -69,17 +74,17 @@
module `fields`.
- **`client.urls.asset` takes an asset Number** as well as an OID
(`assetdetail.v1?Number=S-01001`): the readable form a CLI prints and a
chat message shows (gh-v1, slack bot).
chat message shows (a CLI consumer, a chat-bot consumer).
- **`Outcome.requestId`**: the server's id for a batch, worth logging
(slack bot). Null for an outcome merged from several sends.
(a chat-bot consumer). Null for an outcome merged from several sends.
- `outcomeOf` tolerating a partial envelope is now a stated contract
with a test: consumers' fakes may send only the fields they need.

### Fixed

- `parseViolation`: `tag` is null for the token spelling
(`Violation'Invalid'Epic.Scope`); it was the token, which is not a loc-2
tag (gh-v1).
tag (a CLI consumer).
- `CHANGELOG.md` ships in the npm package.
- Doc notes: `kind: "usage"` means nothing was sent (status 0), so an
error formatter should not blame the server; `client.query`'s resend
Expand All @@ -97,9 +102,9 @@
Alpha releases may break between versions. Every breaking change is listed
here with the one-line migration a consumer applies. A renamed export keeps
its old name as a `@deprecated` alias for one alpha and is removed in the
next; `1.0.0` follows agility-rooms' adoption, and semver after that.
next; `1.0.0` follows the browser app consumer's adoption, and semver after that.

## 1.0.0-alpha.3 — the breaking batch (plan 005, step 2)
## 1.0.0-alpha.3 — the breaking batch

### Breaking

Expand Down
85 changes: 85 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Contributing

This repository is public and the package is published to npmjs. Everything
committed here is readable by anyone, now and in history. The rules below
keep private things out of the tree; `test/unit/publicTree.test.ts` checks
the mechanical parts and `npm run verify` runs it.

## What lives here, and what does not

- `src/`, `test/`, `scripts/`: the SDK and what proves it.
- `specs/`: the invariants of this code (spec 000 and its successors).
A spec describes the SDK and the API it talks to, never a consumer,
a person, a session or an instance.
- `CHANGELOG.md` and `README.md`: what a consumer needs. They ship in
the package. Longer consumer-facing material (examples, guides) goes in
`docs/` when there is some.
- **Working plans and hand-offs do not live here.** Sequencing, surveys of
consumer code, migration steps for a consumer, findings from a session:
all of that goes in the private planning repository (see below) or in
the consumer's own repository. Nothing in this tree links to either.

## Plans and hand-offs

The SDK is developed alongside the projects that consume it, and much of
that work is sequenced in plans and handed between sessions. Those
documents name private repositories, branches, pull requests, sessions
and people, so they are kept out of this repository:

- **Plans** (campaign sequencing, adoption plans, hardening passes) live
in a private planning repository in the product's GitHub organisation
(a maintainer creates it; its name is not written here, by the same
rule), one file per plan as before. A plan may cite this repository's
public history by commit, tag or pull request number; this repository
never cites a plan.
- **Hand-offs** to a consumer project live with the consumer: a file in
that project's repository, or a message to the session working on it.
A session hands work to another session **by message**, not by a file
committed here. The durable copy, if one is wanted, goes in the
planning repository under `handoffs/`.
- **Findings** that change the SDK come back here as issues, pull
requests, tests and changelog lines, worded for the public (see the
next section). The finding's origin stays in the planning repository.

Commit messages follow the same rule: say what changed in the SDK and
why, in terms of the SDK and the API. History before this policy names
consumers and sessions; it is not rewritten (releases are tagged on it,
and consumers pin commits by SHA), and nothing new is added to it.

## How to refer to things

- A consumer project is "a CLI consumer", "a chat-bot consumer", "a
browser app consumer", unless it is itself public, in which case it
may be named and linked.
- The product's source is "the server" or "the product's source". The
hosted instance the integration tier may write to is "the API-testing
instance". No hosted instance's URL is committed, a production one
included: an example base URL uses a fictitious domain (see below).
- People are not named in the tree. A test fixture copied from a live
response keeps the server's shape and carries fictitious identities
(a made-up member, `https://agility.example.com/MyInstance` or
`/VersionOne.Web/` as the example base URL and path).
- Claude sessions are not named. Their ids are attribution for commits,
not content for files.

## Instances

- Unit tests run with nothing. The integration tier writes fixtures and
runs only against a local dev instance, or a hosted instance set aside
for API testing and named in `SDK_TEST_ALLOWED_INSTANCES` (comma-
separated base URLs), set beside the credential by whoever runs it.
Any other hosted URL, and anything named V1Production, is refused.
- `metamodel.json` is pulled by `scripts/download-metamodel.mjs`, which
strips an instance's own definitions and spells every `href` from
`/meta.v1/`, so the committed file is the product's and names no
instance. Run it, then `npm run generate:types`, as a step of each
release.

## Before a pull request

```bash
npm run verify # typecheck, build, unit tests (including the public-tree check)
```

Open the pull request against `proposed_type_script_api_version`. A
change to an invariant changes `specs/000-layers.md` in the same commit.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ npm install @versionone/agility-sdk@alpha
```ts
import { AgilityApiClient } from "@versionone/agility-sdk";

const client = new AgilityApiClient({ baseUrl: "https://www14.v1host.com/v1sdktesting", accessToken: process.env.V1_ACCESS_TOKEN });
const client = new AgilityApiClient({ baseUrl: "https://agility.example.com/MyInstance", accessToken: process.env.V1_ACCESS_TOKEN });
const stories = await client.query({ from: "Story", select: ["Name", "Estimate"], page: { size: 10, start: 0 } });
// stories[0].Estimate is a number | null; stories[0].Nmae does not compile
```
Expand All @@ -35,7 +35,7 @@ npm test
- `npm run typecheck` - run TypeScript type checks
- `npm run verify` - run typecheck + build + tests
- `npm run test` - run unit tests
- `npm run download:metamodel -- <baseAgilityUrl>` - refresh `metamodel.json` from an instance on the current release (instance-specific definitions stripped; `--raw` keeps them), then `npm run generate:types`. The cleanest source is a dev instance on a blank database (Core's gulp task for that); a hosted instance on the release gives the same file. The generated model is from meta.v1 **26.3.1.13** (hosted, 2026-11-12)
- `npm run download:metamodel -- <baseAgilityUrl>` - refresh `metamodel.json` from an instance on the current release (instance-specific definitions stripped; `--raw` keeps them), then `npm run generate:types`. The cleanest source is a dev instance on a blank database (the product's dev tooling has a task for that); a hosted instance on the release gives the same file. The generated model is from meta.v1 **26.3.1.13** (hosted, 2026-11-12)

## Public API

Expand Down Expand Up @@ -112,6 +112,8 @@ an approval, so a pushed tag waits for a maintainer before it stages. A
package must exist before it can be linked, so the very first version was
published once by hand by an `@versionone` org member.

## Before PR
## Contributing

- Run `npm run verify` to validate typecheck, build, and tests before opening a pull request.
Run `npm run verify` (typecheck, build, tests) before opening a pull request.
This repository is public: `CONTRIBUTING.md` says what may be committed
here and where working plans and consumer hand-offs live instead.
Loading
Loading