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
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ This file is intentionally brief. Detailed instructions live in focused docs:
what needs explicit validation.

- Project intent and scope:
- [docs/contributing/project-intent.md](./docs/contributing/project-intent.md)
[docs/contributing/project-intent.md](./docs/contributing/project-intent.md)
- Decision records (check before proposing something already decided against):
[docs/contributing/decisions/index.md](./docs/contributing/decisions/index.md)
- Setup, checks, docs maintenance, preview deploys, and seeding:
- [docs/contributing/setup.md](./docs/contributing/setup.md)
- Code style conventions:
Expand Down
19 changes: 19 additions & 0 deletions docs/contributing/decisions/0000-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# NNNN: Short decision title

- **Status:** accepted <!-- accepted | superseded by [NNNN](./NNNN-slug.md) -->
- **Date:** YYYY-MM-DD

## Context

What question or situation forced the decision. A few sentences on the relevant
system state and constraints, with links to code or docs.

## Decision

What was decided, in one or two sentences. Decisions **not** to build something
count and should be recorded.

## Consequences

What follows: what stays simple, what gets harder, and what would trigger
revisiting. When a preferred future shape is known, name it.
49 changes: 49 additions & 0 deletions docs/contributing/decisions/0001-no-package-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 0001: No user-facing package versioning or import pins

- **Status:** accepted
- **Date:** 2026-07-31

## Context

Every saved package is a real git repo on Cloudflare Artifacts with full commit
history, publish notes (readable via `repo_show_publish_note`), and a clonable
remote (`package_get_git_remote`). Runtime surfaces — `packages.invoke`,
package-owned jobs, apps, services, and webhooks — always resolve the package's
current `entity_sources.published_commit`, and published bundles for non-current
commits are pruned after 30 days. Static cross-package imports
(`kody:@scope/pkg/export`) snapshot the dependency's published commit at the
dependent's publish time and refresh only when the dependent republishes; the
platform never auto-republishes dependents.

The question: should packages get product-level versioning (semver releases, a
versions UI, runnable old versions), and should cross-package imports support an
explicit version/tag/commit pin?

## Decision

No to both. History, diffing, and rollback are served by the package's git repo.
Cross-package imports keep the implicit "snapshot at publish, refresh on
republish" contract, with no pin syntax in specifiers or
`package.json#kody.dependencies`.

Rationale: the consumer of a personal package is almost always its own author,
so there is no downstream consumer needing a semver stability contract; the one
cross-user surface (community forks) already pins by commit
(`community_listings.pinned_commit`, `community_forks.origin_commit`); and
pinned old versions would escape fleet package codemods, which keep published
trees healthy precisely because platform APIs evolve.

## Consequences

- Runtime resolution stays single-pointer (current published commit), and
published-bundle retention stays at 30 days for non-current commits.
- Users who want stability can tag commits in their package repo (labels for
humans, never read by the platform), hold off republishing a dependent until
ready, or fork a dependency into a frozen copy under another name.
- Surfacing what already exists — a publish-history view backed by `git log`
plus publish notes, or a one-click "revert to this publish" — remains open and
cheap; it is UX over existing plumbing, not a versioning system.
- If real demand for import pins appears, the preferred shape is commit-SHA pins
declared in `package.json#kody.dependencies` (not new specifier grammar),
resolved by rebuilding from Artifacts at that commit, with a staleness warning
in repo checks.
22 changes: 22 additions & 0 deletions docs/contributing/decisions/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Decision records

Short architecture decision records (ADRs) for choices that shape the platform,
including decisions **not** to build something. Check here before proposing a
change that may already have been decided.

Decision records are point-in-time documents, so they are exempt from
`npm run docs:check-temporal`; everything else in `docs/` describes current
behavior (see [documentation principles](../documentation.md)).

## Adding a record

1. Copy [`0000-template.md`](./0000-template.md) to the next number with a short
kebab-case slug (for example `0002-some-decision.md`).
2. Keep it to roughly half a page: context, decision, consequences.
3. When a later record changes a decision, mark the old one `superseded by NNNN`
rather than editing or deleting it.
4. Add the record to the list below.

## Records

- [0001 — No user-facing package versioning or import pins](./0001-no-package-versioning.md)
8 changes: 5 additions & 3 deletions docs/contributing/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ notes.

**Exceptions.** Migration and rotation guides (for example
[`secret-rotation.md`](./secret-rotation.md)) may use ordered steps across
deploy phases. Outside those procedures, still describe the current design in
plain language. Quoted validation errors and runtime messages should match what
the product returns, even when the wording contrasts with older manifest shapes.
deploy phases, and [decision records](./decisions/index.md) are point-in-time
documents by design. Outside those procedures, still describe the current design
in plain language. Quoted validation errors and runtime messages should match
what the product returns, even when the wording contrasts with older manifest
shapes.

**Stay lightweight but valuable.** Prefer small, accurate pages over large stale
ones. **Garden** docs when behavior changes: update or delete sections in the
Expand Down
2 changes: 2 additions & 0 deletions docs/contributing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ style, tests, MCP capabilities, and runtime architecture.
## Setup and workflow

- [Getting started](./getting-started.md), [project intent](./project-intent.md)
- [Decision records](./decisions/index.md) (ADRs, including decisions **not** to
build something)
- [Setup](./setup.md), [environment variables](./environment-variables.md),
[setup manifest](./setup-manifest.md)
- [Optional Cloudflare offerings](./cloudflare-offerings.md)
Expand Down
7 changes: 6 additions & 1 deletion tools/check-docs-temporal-language.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { expect, test } from 'vitest'
import {
checkDocumentationTemporalLanguage,
exemptRelativePaths,
exemptRelativePrefixes,
findTemporalLanguageMatches,
listDocumentationPaths,
stripMarkdownCode,
Expand Down Expand Up @@ -138,7 +139,11 @@ test.each([
})

test('exempts principles and migration pages and scans discovered docs', async () => {
for (const relativePath of exemptRelativePaths) {
expect(exemptRelativePrefixes).toContain('docs/contributing/decisions/')
for (const relativePath of [
...exemptRelativePaths,
...exemptRelativePrefixes.map((prefix) => `${prefix}0001-example.md`),
]) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
expect(
findTemporalLanguageMatches({
relativePath,
Expand Down
10 changes: 9 additions & 1 deletion tools/check-docs-temporal-language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export const exemptRelativePaths = new Set([
'docs/contributing/secret-rotation.md',
])

/** Directories whose pages are intentionally point-in-time records. */
export const exemptRelativePrefixes: ReadonlyArray<string> = [
'docs/contributing/decisions/',
]

/**
* Changelog-style phrases to flag in durable documentation.
* Keep this list narrow and aligned with docs/contributing/documentation.md.
Expand Down Expand Up @@ -150,7 +155,10 @@ export function findTemporalLanguageMatches(input: {
content: string
}): TemporalLanguageMatch[] {
const relativePath = input.relativePath.replaceAll('\\', '/')
if (exemptRelativePaths.has(relativePath)) {
if (
exemptRelativePaths.has(relativePath) ||
exemptRelativePrefixes.some((prefix) => relativePath.startsWith(prefix))
) {
return []
}

Expand Down
Loading