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
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ All notable changes to Tabellio are recorded here.

## Unreleased

## 0.3.0 - 2026-07-15

### Added

- Deterministic `tabellio-preflight` checks for Node, GitHub remotes, GitHub CLI authentication, platform configuration, Entire version and enablement, required Codex hooks, hook trust, and release-main cleanliness.
- Exact `/hooks` recovery guidance when Entire integration exists but Codex has not trusted the repository hook commands.
- Integrity-bound `tabellio-release-operation/v0.1` plans and short-lived release approvals.
- Resumable, idempotent post-merge release execution for private control-ref publication, annotated tag publication, and GitHub release creation.
- Isolated consumer-repository dogfood covering release planning, exact validation, terminal review sync, control transport, tag publication, failure recovery, and GitHub release invocation.

### Changed

- Release planning now runs exact merged-head validation and terminal review synchronization before requesting remote-write approval.
- Pull-request merge remains an explicit operator gate; release execution begins only after the resulting commit and all publishable control OIDs are known.

### Release Gates

- `tabellio-preflight --profile release`
- `npm run check`
- Fallow whole-repository dead-code and stale-suppression scan
- Fallow changed-code audit against `origin/main`
- `npm pack --dry-run --json`
- Exact merged-head Tabellio validation

## 0.2.0 - 2026-07-15

### Added
Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ Avoid:

Before tagging a release:

- run `node scripts/tabellio-preflight.mjs --profile release`
- update `CHANGELOG.md`
- confirm README examples use the intended release tag
- run local checks
- run an exact-head `tabellio-validate` pass
- confirm the durable review cycle is ready
- tag from a clean `origin/main` commit

After the release PR is explicitly merged, use `tabellio-release plan` to run merged-head validation, synchronize the terminal review cycle, and bind exact publishable control refs. Review the generated intent, create a short-lived `tabellio-release-approval/v0.1`, then run `tabellio-release execute` once. The executor publishes control refs, the annotated tag, and the GitHub release. It never merges the pull request.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ AI-assisted pull requests should not depend on reviewer trust alone. Tabellio gi
| `scripts/lib/review-cycle.mjs` | Durable GitHub and agent review/fix state machine |
| `scripts/lib/validation-runner.mjs` | Exact-commit, shell-free validation with bounded evidence logs |
| `scripts/lib/control-ref-transport.mjs` | Approval-gated, fast-forward-only sharing of review, validation, and Entire refs |
| `scripts/tabellio-preflight.mjs` | Fail-closed GitHub, Entire, hook-trust, and release-main readiness checks |
| `scripts/tabellio-release.mjs` | Integrity-bound post-merge control-ref, tag, and GitHub release orchestration |
| `scripts/lib/` | Git process, repository contract, worktree, and context primitives |
| `scripts/` | Dependency-free capture, writer, and validators |
| `examples/` | Minimal valid context, evidence, review, validation, stack, and ledger fixtures |
Expand All @@ -103,6 +105,15 @@ node scripts/tabellio-validate.mjs run --repo . --commit HEAD --manifest tabelli

Keep `origin` limited to ordinary code branches and tags. Configure a separate private GitHub repository under another remote name before publishing control refs. The transport refuses to target `origin`.

Before agent or release work, run:

```bash
node scripts/tabellio-preflight.mjs --profile agent
node scripts/tabellio-preflight.mjs --profile release
```

Hook trust failures identify the exact Codex `/hooks` action required. Release profile additionally requires clean `main` equal to `origin/main`.

Validate the bundled fixture:

```bash
Expand Down
33 changes: 33 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,39 @@ node scripts/tabellio-control-ref.mjs plan \

`TABELLIO_CONTROL_REMOTE` must name a separately configured private GitHub repository remote. It cannot be `origin`. Create a matching `tabellio-control-ref-approval/v0.1` document after reviewing the exact local and remote OIDs, then execute it once with `tabellio-control-ref.mjs execute`. Multi-ref publication is atomic. Non-fast-forward publication, divergence, changed refs, expired approvals, and reused approvals fail closed.

## Preflight And Release

Run preflight before agent work and again from clean merged `main`:

```bash
node scripts/tabellio-preflight.mjs --profile agent
node scripts/tabellio-preflight.mjs --profile release
```

`entire doctor` must report healthy metadata and trusted Codex hooks. When trust is missing, open `/hooks` in Codex and approve the four repository hooks.

After explicit PR merge, create the exact release plan:

```bash
node scripts/tabellio-release.mjs plan \
--owner example \
--remote-repo repository \
--number 42 \
--version 0.3.0 \
--notes docs/releases/v0.3.0.md \
--out /tmp/tabellio-release-intent.json
```

Review the intent and create a short-lived `tabellio-release-approval/v0.1` bound to `integrity.digest`. Then execute:

```bash
node scripts/tabellio-release.mjs execute \
--intent /tmp/tabellio-release-intent.json \
--approval /secure/tabellio-release-approval.json
```

Planning accepts only the validation manifest named by `tabellio.platform.json`. It runs commands on the exact merged commit and binds checkpoint evidence to the pre-merge pull-request head, so a squash merge does not erase proof. Execution accepts canonical HTTPS, SCP-style SSH, and `ssh://git@github.com/...` remotes, resolves Git fetch/push URL rewrites, rechecks the distinct repository identities and current private control visibility, then publishes exact private control refs, a deterministic annotated tag, and the GitHub release. Merge stays outside this command because the final squash commit must exist before release approval can bind it.

## Local Validation

From this repository:
Expand Down
6 changes: 4 additions & 2 deletions docs/operations-hardening.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ Do not maintain a second merge authority. Independent squash or rebase merges cr

## Control-State Publication

Review cycles, validation results, and Entire checkpoints are published together with `git push --atomic`, explicit force-with-lease expectations, and a one-use approval. Publication rejects non-fast-forward updates, divergence, changed local or remote object IDs, expired approvals, and reused approval IDs.
Review cycles, validation results, and Entire checkpoints are published together with `git push --atomic`, explicit force-with-lease expectations, and a one-use approval. Publication permits refs that were already unchanged at planning while rejecting partial publication, non-fast-forward updates, divergence, changed local or remote object IDs, expired approvals, and reused approval IDs. Release retries derive a fresh one-use control approval from the still-active release approval after re-verifying the exact repositories and OIDs.

Automatic Entire session pushes to `origin` remain disabled. The approved control-ref transport publishes `refs/heads/entire/checkpoints/v1` with the review and validation refs only to a separately configured private GitHub repository. Planning and execution reject `origin`.
Automatic Entire session pushes to `origin` remain disabled. The approved control-ref transport publishes `refs/heads/entire/checkpoints/v1` with the review and validation refs only to a separately configured private GitHub repository. Planning and execution reject `origin`, reject repository aliases, and recheck private visibility immediately before publication.

Release planning is local and credentialed-read-only: it requires clean merged `main`, runs exact-head commands from the platform-configured manifest, binds checkpoint evidence to the exact pre-merge pull-request head, requires durable proof that that head reached `ready`, rejects new terminal feedback or failed checks, binds the effective fetch and push targets for both GitHub remotes, and snapshots the resulting control OIDs. Remote publication requires a separate release approval capped at one hour. Execution re-verifies case-normalized code identity plus control identity and privacy before every write, writes an atomic local receipt before each phase, and reruns idempotent reconciliation for control refs, exact annotated-tag objects, and releases after failures or prior success. Pull-request merge remains a separate explicit action because an approval cannot safely bind a squash commit that does not exist yet.

## Production Checklist

Expand Down
17 changes: 17 additions & 0 deletions docs/releases/v0.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Tabellio v0.3.0 makes release readiness and publication deterministic.

## Highlights

- `tabellio-preflight` catches missing or untrusted Entire hooks before commit or release work begins.
- Release planning binds merged Git state, pre-merge head readiness, the platform validation manifest, both distinct GitHub repository identities, private control refs, release notes, and package version into one integrity-protected intent.
- One approved post-merge execution publishes exact control refs, a canonical annotated Git tag, and the GitHub release.
- Release receipts survive partial failures, re-verify effective fetch and push targets on every retry, and rerun idempotent publication reconciliation even after an earlier success so rolled-back remote state cannot be skipped.
- Isolated repository tests prove the complete plan and publication workflow without touching live GitHub resources.

Merge remains an explicit operator action. This prevents a release approval from authorizing an unknown squash commit.

For squash merges, validation commands run against the exact merged commit while checkpoint evidence remains bound to the separately recorded pull-request head. Annotated tags use one canonical Git object derived from the approved commit, tag name, title, timestamp, and automation identity; retries reject any metadata drift. The CLI returns the schema-valid receipt and its local `receiptPath` as separate fields.

Review cycles now emit `tabellio-review-cycle/v0.3`, which records head-bound `ready` events. Validation results now emit `tabellio-validation-result/v0.2`, which requires the checkpoint revision separately from the validated revision. Runtime readers still accept v0.2 review cycles and v0.1 validation results so existing private ledgers can migrate on their next write.

Released review-cycle and validation-result schemas remain available under immutable versioned filenames. Release planning binds terminal review sync to the exact pull-request head. Execution requires all three canonical control refs, pins the verified control push URL, stores custom receipt state outside the worktree, rechecks approval expiry before every publish phase, rechecks live `origin/main` immediately before tag publication, and revalidates the canonical remote tag before creating or accepting a GitHub release.
4 changes: 2 additions & 2 deletions docs/review-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Review status is deterministic:
| `update_required` | Fix exists locally but is not in the remote PR head |
| `blocked` | A remote check failed or GitHub reports the change as non-mergeable |
| `validating` | No validation result exists yet, or checks are pending/running |
| `ready` | Feedback is handled, fixes are published, and checks are clear |
| `ready` | Feedback is handled, fixes are published, checks are clear, and a head-bound readiness event is recorded |
| `merged` / `closed` | Pull-request terminal state |

## Sync GitHub
Expand Down Expand Up @@ -83,7 +83,7 @@ git-spice restacks rewrite commit IDs. Tabellio retains `originalCommit` and rem

## Storage And Transport

Ledger writes create normal Git blobs, trees, and commits without changing the working tree. Concurrent writers use compare-and-swap on `refs/tabellio/reviews`; stale writers fail instead of overwriting newer state. The same implementation works in normal and bare repositories. The latest cycle retains the newest 100 audit events; older versions remain recoverable from the ledger's Git commit history.
Ledger writes create normal Git blobs, trees, and commits without changing the working tree. Concurrent writers use compare-and-swap on `refs/tabellio/reviews`; stale writers fail instead of overwriting newer state. The same implementation works in normal and bare repositories. The latest `tabellio-review-cycle/v0.3` cycle retains the newest 100 audit events; older versions remain recoverable from the ledger's Git commit history. A `ready` event stores the exact pull-request head commit so release planning can prove readiness existed before the terminal merged sync, and sync preserves that evidence when the event window is full. Terminal sync migrates a legacy v0.2 `ready` cycle into this evidence form, while newly observed feedback or failed checks still block release.

To share the ledger, configure a separate private GitHub repository as the control-state remote, for example:

Expand Down
2 changes: 2 additions & 0 deletions docs/validation-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ The runner:
7. Removes the worktree even after failure.
8. Writes an integrity-protected result to `refs/tabellio/validations` with compare-and-swap retries.

New results use `tabellio-validation-result/v0.2`. They require `checkpointRevision` so checkpoint proof remains bound to the pull-request head when the validated revision is a later squash-merge commit. Runtime readers continue to accept legacy v0.1 results.

Read the newest result for a commit:

```bash
Expand Down
74 changes: 74 additions & 0 deletions examples/tabellio-release/minimal-intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"schemaVersion": "tabellio-release-operation/v0.1",
"repository": {
"id": "github.com/example/repository",
"owner": "example",
"name": "repository"
},
"version": "0.3.0",
"tag": "v0.3.0",
"revision": {
"commit": "dddddddddddddddddddddddddddddddddddddddd",
"parent": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
},
"pullRequest": {
"number": 42,
"headCommit": "ffffffffffffffffffffffffffffffffffffffff",
"mergeCommit": "dddddddddddddddddddddddddddddddddddddddd"
},
"code": {
"remote": "origin",
"branch": "main"
},
"control": {
"repository": {
"id": "github.com/example/repository-control"
},
"intent": {
"schemaVersion": "tabellio-control-ref-operation/v0.1",
"operation": "publish",
"repository": {
"id": "github.com/example/repository"
},
"remote": "control",
"refs": [
{
"name": "refs/tabellio/reviews",
"localOid": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"remoteOid": null
},
{
"name": "refs/tabellio/validations",
"localOid": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"remoteOid": null
},
{
"name": "refs/heads/entire/checkpoints/v1",
"localOid": "cccccccccccccccccccccccccccccccccccccccc",
"remoteOid": null
}
],
"createdAt": "2026-07-15T12:00:00.000Z",
"integrity": {
"algorithm": "sha256",
"digest": "4d0cf553f4bf4b9bcc860e1e3ae2f7142359cc9d2d7b5666d3a9b6596f43de6b"
}
}
},
"validation": {
"runId": "validation-example",
"resultVersion": "1111111111111111111111111111111111111111",
"status": "passed",
"headCommit": "dddddddddddddddddddddddddddddddddddddddd"
},
"release": {
"title": "Tabellio v0.3.0",
"notesPath": "docs/releases/v0.3.0.md",
"notesDigest": "2222222222222222222222222222222222222222222222222222222222222222"
},
"createdAt": "2026-07-15T12:00:00.000Z",
"integrity": {
"algorithm": "sha256",
"digest": "7a5bbc64a02e37356acb82576698584e8e21f3f0539a6a572d1cf8083a6b7c7c"
}
}
27 changes: 18 additions & 9 deletions examples/tabellio-review/minimal-cycle.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"schemaVersion": "tabellio-review-cycle/v0.2",
"schemaVersion": "tabellio-review-cycle/v0.3",
"id": "review-example-001",
"repository": { "id": "example/tabellio" },
"provider": { "id": "github", "owner": "example", "repo": "tabellio" },
Expand Down Expand Up @@ -35,17 +35,26 @@
"updatedAt": "2026-07-10T12:00:00.000Z"
}]
},
"events": [{
"id": "event-example-001",
"type": "synced",
"actor": "example-agent",
"at": "2026-07-10T12:00:00.000Z",
"detail": "Synced 0 feedback items at example head."
}],
"events": [
{
"id": "event-example-001",
"type": "synced",
"actor": "example-agent",
"at": "2026-07-10T12:00:00.000Z",
"detail": "Synced 0 feedback items at example head."
},
{
"id": "event-example-002",
"type": "ready",
"actor": "example-agent",
"at": "2026-07-10T12:00:00.000Z",
"detail": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
],
"createdAt": "2026-07-10T12:00:00.000Z",
"updatedAt": "2026-07-10T12:00:00.000Z",
"integrity": {
"algorithm": "sha256",
"digest": "88a01451f8b6e7f9d7df66c89dcc0e44234dcc89f78c18e5fef3ca750341e040"
"digest": "4bc32c6f6161c4c3a2bb22924e7587734c579bf6edb4b056615cbd9737385985"
}
}
9 changes: 7 additions & 2 deletions examples/tabellio-validation/minimal-result.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
{
"schemaVersion": "tabellio-validation-result/v0.1",
"schemaVersion": "tabellio-validation-result/v0.2",
"runId": "validation-example-001",
"repository": { "id": "example/tabellio" },
"revision": {
"baseCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"mergeBase": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"headCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"checkpointRevision": {
"baseCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"mergeBase": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"headCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"suite": {
"id": "tabellio-default",
"manifestPath": "tabellio.validation.json",
Expand Down Expand Up @@ -44,6 +49,6 @@
"completedAt": "2026-07-10T12:00:01.000Z",
"integrity": {
"algorithm": "sha256",
"digest": "e0bf535d574ca275855e3fee7cccc4512785eaa9f4041975459d898145a4338f"
"digest": "eefca7fe3e063446cfdc3151ad9238c5015e577741e3023ffb2e0d06fcbf194d"
}
}
Loading