Skip to content

docs: say what stride is, and what 1.0 actually promises - #222

Merged
eschizoid merged 5 commits into
mainfrom
docs/216-217-identity-and-contract
Aug 21, 2026
Merged

docs: say what stride is, and what 1.0 actually promises#222
eschizoid merged 5 commits into
mainfrom
docs/216-217-identity-and-contract

Conversation

@eschizoid

Copy link
Copy Markdown
Owner

Closes #216. Closes #217.

Two ADR 0000 changes that had to ship together because they edit the same file, and because the second one gates the payload work behind it.

§9b — what stride IS

ADR 0000 stated its boundary almost entirely in the negative. §10 is a list of refusals; the only positive line was the one-sentence thesis at the top. Nothing said stride is the deterministic local source of athlete state, that data providers are inputs rather than the identity, or that the reasoning layer is interchangeable by construction.

The cost is measurable rather than theoretical. A careful outside reviewer read this repo and wrote a page proposing, as future direction, an architecture stride already ships — provenance, confidence tiers, plan and adherence memory, null semantics, versioned schemas, rep-level progression, the engine/coach boundary. Then proposed two things §10 and ADR 0006 explicitly rule out, without engaging the recorded reasoning, because nothing pointed at it.

The section is deliberately three consequences rather than a manifesto, and every refusal in §10 now follows from one of them. MCP server in particular stops reading as an oversight and becomes the claim it always was: the CLI plus versioned JSON already IS the agent interface. §9b points at §10 and does not restate it — ADR 0000 already records that a second overlapping scope list existed once and neither was a superset of the other.

§9c — the 1.0 compatibility contract

Six surfaces version independently and they are not one promise. Most of the table is recording what is already true. One row needed deciding.

Adding a payload key was documented two ways that appear to contradict. The envelope rule says adding a field does not bump the version, because "a consumer reading known keys is unaffected by a new one appearing". Every schema carries additionalKeys: false, so anything validating a payload against the checked-in schema fails the moment a key is added.

Both are true. consumer means two different things.

The 1.x resolution: the schemas are stride's contract with itself, not a closed-world validator for third parties. additionalKeys: false exists so a payload that GREW without a schema update fails CI — that is its entire job, and it is why just schema-check runs against a real database. A downstream consumer reads the keys it needs and validates its own required subset; stride does not promise its schema works as a closed-world check.

So adding a key is additive, removing or retyping bumps json_schema_version, and a change that breaks a reader of known keys is a v3/ directory rather than an edit to v2/. Error codes are additive in 1.x, since the vocabulary is published and diffed against source in CI.

This is why #217 sequences before #219 and #221. Both add payload keys. Deciding whether that is breaking after shipping the additions is backwards.

§6's breaking-is-fine permission now states that it expires at 1.0 and points at §9c, instead of sitting ambient where a reader meets it first.

Also in this PR

PLAN.md — a 1.0 order-of-work breadcrumb, added against AGENTS.md's standing advice and deliberately. The last plan file rotted because it restated work that lived in issues, so the copies drifted and a watch-item it tracked fired unnoticed. This one holds no work: only the order, the two constraints that set it, and pointers. It says to delete itself when the phases merge.

And a skill fix a 1.0 readiness audit surfaced: the workflow line sold stride plan as "summary + open sessions + last-14d activities", omitting plan_history_28d and adherence_28d — the two fields that make the coaching loop closed rather than a fresh guess each week. The payload table forty lines below already documented both, so the contract was right and the instruction was not.

Docs only. just issue-claims clean at 1887 blocks.

…tract

PLAN.md exists against AGENTS.md's standing advice, deliberately and in a
shape that answers why that advice was given. The last plan file rotted
because it restated work that lived in issues, so the copies drifted and
nothing read either; a watch-item it tracked fired unnoticed. This one
holds no work — only the ORDER and the two constraints that set it, which
is the part GitHub cannot carry. Every item is a pointer, and the file
says to delete itself when the phases merge.

The order is not arbitrary. #217 has to decide whether adding a payload
key is breaking before #219 and #221 add keys — the envelope docs say it
is not, every schema carries additionalKeys: false, and those cannot both
be true for a consumer validating against one. And #216/#217 share
ADR 0000 while #218/#219/#138 share app.roc, which is why the phases are
sequential rather than parallel.

Also fixes the plan drift a 1.0 audit found. The skill's workflow line
sold `stride plan` as "summary + open sessions + last-14d activities",
omitting plan_history_28d and adherence_28d — the two fields that make
the coaching loop closed rather than a fresh guess each week. The payload
table forty lines down already documented both, so the contract was
right and the instruction was not.
Closes #216. Closes #217.

ADR 0000 stated its boundary almost entirely in the negative — section 10
is a list of refusals, and the only positive line was the thesis at the
top. Section 9b now says what stride IS: the deterministic local source
of athlete state, with data providers as inputs rather than the identity,
and a reasoning layer that is interchangeable by construction. Every
refusal in section 10 follows from one of those three consequences, which
turns the MCP entry from an apparent oversight into the claim it actually
is: the CLI plus versioned JSON already IS the agent interface.

The cost of not having said that is in the issue and is measurable. A
careful reader wrote a page proposing an architecture stride already
ships, then proposed two things sections 10 and ADR 0006 rule out,
without engaging the reasoning, because nothing pointed at it.

Section 9c defines the 1.0 contract across the six surfaces that version
independently. The one that needed deciding rather than recording:
adding a payload key is documented as non-breaking because "a consumer
reading known keys is unaffected", while every schema carries
additionalKeys: false, so anything validating against one fails the
moment a key appears. Both were true; consumer meant two different
things. The resolution is that the schemas are stride's contract with
ITSELF — additionalKeys: false exists so a payload that GREW without a
schema update fails CI, which is why schema-check runs against a real
database — and a downstream consumer validates its own required subset
rather than treating ours as closed-world. So adding a key stays
additive, removing or retyping bumps the envelope, and a change that
breaks a reader of known keys is a v3 directory rather than an edit to
v2.

Error codes are additive in 1.x: the vocabulary is published and diffed
against source in CI, so a caller branching on a code keeps working.

Section 6's breaking-is-fine permission now says it expires at 1.0 and
points at 9c, rather than sitting ambient where a reader meets it first.
… above

Review found ten things. Four mattered.

Section 9c said `additionalKeys: false` "is why `just schema-check` runs
against a real database". schema-check runs in NO CI job — `grep -rn
schema-check .github/` returns nothing. CI's enforcement is `just e2e`
running validate.jq against seeded fixtures; schema-check is the local
"does MY data conform" pass, and the causality is inverted: it runs
against a real database BECAUSE CI's fixtures are not real data. Three
other files state the division correctly and this ADR was the only one
fusing them. The section now names e2e as the CI mechanism, and records
that coverage is not total — complete, import, init, rate and sync are
validated by neither pass, tte only by the local one, so a key added
there is caught by nothing today.

Then the PR added PLAN.md while the same file, forty lines below the new
sections, said there is deliberately no scratch plan file. AGENTS.md
carried the twin. Both now draw the line where it actually belongs: no
plan file DESCRIBES work, sequencing and its constraints are the one
thing issues cannot carry, and the file must be pointers, scanned by the
drift guard, and self-deleting.

Which it was not. PLAN.md is the file in this repo densest with
issue-state claims and `tools/issue-claims.sh` did not scan it — the
guard that shipped two commits ago for exactly this class. Added:
1887 blocks and 1 checked ref becomes 1899 and 5.

And PLAN.md gave #188's blocker as "one Run in the database", which is
the argument the issue itself retracts — a per-athlete measurement cannot
rank a feature aimed at the circle. The real blocker is that no mean-max
SPEED curve exists: the power path stores best_5s_w through best_3600s_w
at analyze time and the pace equivalent has no columns and no producer.

Smaller, same review: "every refusal in section 10 follows from one of
them" overclaimed — graphs is an empirical result, social is a market
fact, medical is a limit on the data. The error-code diff asserts set
EQUALITY, so it enforces sync rather than additivity, and the unguarded
count of 43 came out with it. #219 RETYPES `commands` rather than adding
a key, which under 9c's own rule is the heavier change. "Six surfaces
version independently" — only three carry a version; the other three are
why the section is needed. And "no API key" now says no MODEL API key,
since section 1 stores Strava tokens.
Round 2 answered the question I asked it — is PLAN.md rot-resistant, or
did I just write a rule it will violate — and the answer was the second.

Three files in the last commit published "the moment it describes what a
ticket contains, it has become the file that rotted". PLAN.md's #188 row,
which that same commit rewrote, was sixty words of ticket contents
including two symbol names copied from an issue comment. It was twelve
words before I "fixed" it. More accurate and five times more of a
violation.

Worse, the guard I added covered the wrong half. All four refs it
resolves come from the Parked table, because that is the only block
pairing a state phrase with a ref. The ten refs in the phases and feature
track carry no state phrase and therefore no oracle — the guard covered
the four tickets least likely to move and none of the sequencing the file
exists to hold. And the state column would have shipped 1/4 wrong on
merge, with no mechanism to update it, which is exactly how the last plan
file survived.

So the file is now what justified it and nothing else: two ordering
constraints and a four-line order. Twenty-seven lines. The Parked table
went because every fact in it lives on its own issue, the standing rules
went because a workflow is not a plan and a Copilot observation is a
snapshot, and the state column went because a column nobody updates is
worse than no column.

Three smaller ones from the same round:

The section-10 attribution replaced "every refusal follows" with a new
unearned claim — that graphs, social and medical all come from the header
— when the header speaks to none of graphs' grounds. A graph renders
deterministic provenanced facts perfectly well; the reason it is refused
is that the experiment ran and failed. Now each exception carries its own
ground.

The coverage sentence swept `tte` into "caught by nothing" when the
clause before it says the local recipe covers `tte`. It is caught by
nothing IN CI.

And the plan-file rule was published twice with different conditions —
AGENTS.md required the drift guard, the ADR did not. The dropped
condition was the only one with a mechanism, and it is the one that
round implemented. A twin divergence introduced by the fix for a twin
divergence.
Round 3 returned MERGE and handed over something better than a fix: the
one condition with no mechanism now has one.

"Delete this file when the sequence is done" was a request. That is
precisely how `.claude/PLAN.md` survived long enough to rot — nothing
failed when it outlived its purpose. PLAN.md now says it remains open
until #220 lands, which `just issue-claims` reads as a state claim about
an issue. On the day #220 closes, CI goes red naming this file.

Mutation-proved rather than asserted: with #220 open the run is clean at
1895 blocks and 2 claims resolved; substituting a closed ref gives
STALE PLAN.md:8. Before this the file contributed zero checked refs,
because "must land before" matches nothing in the pattern.

Also closes the wording gap round 3 flagged in the rule itself. Both
files paired a permission — sequencing AND ITS REASONS — with an absolute
that forbade naming a ticket's contents at all. You cannot say why two
tickets conflict without naming the fact that makes them conflict, so the
absolute was worded past what the permission allows. Both now scope it to
the one fact that creates the constraint.

And the section rename left two pointers to the old name behind.
@eschizoid
eschizoid merged commit 289ab41 into main Aug 21, 2026
9 checks passed
@eschizoid
eschizoid deleted the docs/216-217-identity-and-contract branch August 22, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant