diff --git a/.gitpin/change-evidence.json b/.gitpin/change-evidence.json index 6ff75fb..ad599e6 100644 --- a/.gitpin/change-evidence.json +++ b/.gitpin/change-evidence.json @@ -1,58 +1,68 @@ { "schemaVersion": 1, - "summary": "Rewrite the README for a fast first read (problem, 30-second fail-to-pass quickstart, film, Mermaid trust-boundary sketch, guardrails, stage-neutral status) and add the social preview and demo GIF it references.", + "summary": "Add the verification pack in docs/verification: overview, functional, technical trace, coverage matrix, executed test evidence at 90598a1, demos and readiness status block.", "claims": [ { - "id": "README-FIRST-READ", - "statement": "README.md now leads with the problem, a pnpm demo:gate quickstart, use-in-your-repo steps, a Mermaid sketch of the base-trusted policy and head-submitted manifest, guardrails and non-goals, and keeps the stage-neutral 0.6.4 sentence required by scripts/verify-release-tag.mjs.", + "id": "VERIFICATION-PACK", + "statement": "docs/verification adds the seven-file verification pack; READINESS.md carries the machine-readable status block with the verdict Verified End-to-End Ready at 90598a1.", "covers": [ - "README.md" + "docs/verification/README.md", + "docs/verification/FUNCTIONAL.md", + "docs/verification/TECHNICAL.md", + "docs/verification/COVERAGE.md", + "docs/verification/TESTS.md", + "docs/verification/DEMOS.md", + "docs/verification/READINESS.md" ], "evidence": [ { "ref": "head", - "path": "README.md", - "lineStart": 38, - "lineEnd": 38, - "contentSha256": "d3088a4ed3623c123bbc6615dce53fc46ac9c61666bd84fd1231e0c49ba82132" + "path": "docs/verification/README.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "ada84a7dbfea9df76535cceae661813128205f2acc4ea7be595e24b1d1fb35c7" }, { "ref": "head", - "path": "README.md", - "lineStart": 43, - "lineEnd": 43, - "contentSha256": "a0a4d4a60b7eb8343c9a0021ec8237f9cbdc088d03c4c6bcd0ffd4c70d92d67c" + "path": "docs/verification/FUNCTIONAL.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "c928217b815376b9f2d2ce0f9fb011c9df6386763815c8cb37b2e37c4c01208f" }, { "ref": "head", - "path": "README.md", - "lineStart": 184, - "lineEnd": 184, - "contentSha256": "06fa519f1345a104f9fc86974955bc236498b11ae67e363335cc2887a94efef2" - } - ] - }, - { - "id": "README-MEDIA", - "statement": "The social preview image and the demo GIF are referenced from the README; the GIF is cut from the delivered 30-second film and the preview is rendered from a real film frame.", - "covers": [ - "docs/media/social-preview.png", - "docs/media/gitpin-demo.gif" - ], - "evidence": [ + "path": "docs/verification/TECHNICAL.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "83e4d6c9ba3b0bf03981e38e26428eabf0f0c42492928ac955439f0b3d243f16" + }, + { + "ref": "head", + "path": "docs/verification/COVERAGE.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "9612e9419400d1830d20d86b7855f616c1868f6ac937a45dd1849c6596d6d1aa" + }, + { + "ref": "head", + "path": "docs/verification/TESTS.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "2e2e79ae1f9d53c9af70739be09176e10ac0055d51ff48fedf380dff075eb6ae" + }, { "ref": "head", - "path": "README.md", - "lineStart": 3, - "lineEnd": 3, - "contentSha256": "8a856a365aea603fe62f31cbd6e285d0acb04f352a6667701f760ea7813cf968" + "path": "docs/verification/DEMOS.md", + "lineStart": 1, + "lineEnd": 1, + "contentSha256": "6d8a9ba3e9e53a7fe3e582bb182ca12e693515297547f5fa8ed6ae3daf2a8c3b" }, { "ref": "head", - "path": "README.md", - "lineStart": 68, - "lineEnd": 68, - "contentSha256": "98a2d7c9416a700e619964b9069a5a7f6d7892ff032e6e0e4450f38c818cefd1" + "path": "docs/verification/READINESS.md", + "lineStart": 5, + "lineEnd": 5, + "contentSha256": "67c03c92e2f38217b1dc4f8b0aeaac2835f2d73e7c9a5a8f43fd48511b5a77cf" } ] } diff --git a/docs/verification/COVERAGE.md b/docs/verification/COVERAGE.md new file mode 100644 index 0000000..a7a916a --- /dev/null +++ b/docs/verification/COVERAGE.md @@ -0,0 +1,57 @@ +# GitPin Coverage Matrix + +Scope: GitPin 0.6.4. The code was read at `e4c550e`. The suites ran at main `90598a1` (2026-10-15); the commits since `e4c550e` change HTTP loopback binding (#112), the CI trigger (#113) and the README (#111). Rungs use the evidence ladder: Planned, Implemented, Integrated, Tested, Demonstrated, Production-Verified. + +How to read the rung column: it is the highest rung this pack can support from what was read or checked. Source and file paths give Implemented. A live trace through the pieces gives Integrated; the published `gitpin@0.6.4` gate was run against this repository's own history (see [TESTS.md](TESTS.md#production-smoke-checks-2026-10-15)). "Tested" needs an executed command with a result; the test run column points to [TESTS.md](TESTS.md). "Demonstrated" comes from the delivered films, listed in [DEMOS.md](DEMOS.md). "Production-Verified" needs a deployed SHA plus a health check and a smoke test of the path; the deploy column lists what was checked. + +## Evidence matrix + +| Capability | Journey | Surface | Logic module | Integration | Test files | Test run | Demo | Deploy status | Rung | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| PR evidence gate (`gitpin gate --base --head`) | Run gate locally; fix failed gate | CLI | `src/gate.ts`, `src/gate-policy.ts`, `src/gate-types.ts`, `src/cli-gate.ts` | Local `git` only | `src/gate.test.ts`, `src/gate-hero.test.ts` | see TESTS.md | see DEMOS.md: gate fails on `uncovered-change` at exit 1, a claim with line + sha256, gate passes at exit 0, plain-git sha256 recheck, `gate.ts` coverage code | npm `gitpin` 0.6.4 matches main's version (checked `npm view`) | Production-Verified (npm 0.6.4 smoke); Tested; Demonstrated | +| Coverage by merge-base diff and `uncovered-change` | Fix a failed gate | CLI | `src/gate.ts:48-60,169-173` | Local `git` | `src/gate.test.ts`, `src/gate-hero.test.ts` | see TESTS.md | see DEMOS.md | as above | Production-Verified (npm smoke: `uncovered-change` on 2 paths); Tested; Demonstrated | +| Base policy, `policyChanges: block`, limits | Install policy; run gate | CLI, Action | `src/gate-policy.ts`, `src/gate.ts:61-67` | Local `git` | `src/gate.test.ts` (base policy, blocked policy change) | see TESTS.md | see DEMOS.md (policy trust boundary is not named in the facts given; check there) | as above | Tested | +| Per-claim line-slice sha256 verify | Run gate; plain-git recheck | CLI, Action | `src/gate.ts:108-115` | Local `git` | `src/gate.test.ts` (hash mismatch, deleted file, replace refs) | see TESTS.md | see DEMOS.md | as above | Production-Verified (npm smoke: `evidence-verified`); Tested; Demonstrated | +| Hero fixture `pnpm demo:gate` | Hero demo | CLI | `scripts/demo-gate-hero.mjs` | Synthetic scratch git repo | `src/gate-hero.test.ts` | see TESTS.md | see DEMOS.md | Local only; labelled synthetic | Tested; Demonstrated | +| GitHub Action | Run gate in CI | Action | `action.yml`, `scripts/render-gate-action-report.mjs` | npm registry (installs `gitpin@0.6.4`), GitHub runner | `src/gate-action.test.ts`; self-run via `.github/workflows/evidence-gate.yml` | see TESTS.md | Not in the films' list above | The repo's own evidence gate passed on PRs #111 and #113 | Integrated; Tested | +| 12 `pin.*` MCP tools, stdio | Connect client; prove; verify | MCP | `src/server.ts`, `src/pin-tools.ts`, `src/wiki*.ts`, `src/git*.ts` | MCP SDK, local Git | `src/wiki.test.ts`, `src/git.test.ts`, `src/evidence.test.ts`, `src/structure.test.ts` | see TESTS.md | Not shown in the two films | npm 0.6.4; no real MCP client session checked | Tested | +| MCP over HTTP (bearer, host allowlist, health) | Serve over HTTP | MCP | `src/http.ts` | MCP SDK Streamable HTTP | `src/http.test.ts` | see TESTS.md | Not shown | Container and remote deploy scripts exist (`Dockerfile.remote`, `scripts/verify-remote.mjs`, `deploy/`); no deployed instance checked | Tested; works locally only (no deployed instance) | +| Evidence prove / prove-set | Prove a claim | MCP, CLI | `src/evidence-prove.ts`, `src/evidence-set.ts`, `src/evidence-citation.ts` | Local Git | `src/evidence.test.ts` | see TESTS.md | Not shown | npm 0.6.4 | Tested | +| Evidence verify / verify-set / verify-cites | Verify a claim; verify citations | MCP, CLI | `src/evidence-verify.ts`, `src/cli-verify.ts` | Local Git | `src/evidence.test.ts`, `src/cli.test.ts` | see TESTS.md | Plain-git recheck appears in the films (see DEMOS.md) | npm 0.6.4 | Tested | +| Setup and health (`init`, `doctor`, `brief`) | Connect an MCP client | CLI | `src/onboarding.ts`, `src/doctor.ts`, `src/context-brief.ts` | Client config files on disk | `src/onboarding.test.ts`, `src/doctor.test.ts` | see TESTS.md | Not shown | npm 0.6.4 | Tested | +| Pages site | View product page | Site | `site/`, `scripts/build-site.mjs` | GitHub Pages | `tests/browser/site.spec.mjs`, `tests/browser/analytics-privacy.spec.mjs`, `src/site-build.test.ts` | see TESTS.md | Not shown | Redeployed from `90598a1` on 2026-10-15 (run 38737568718; the live footer had still shown the attribution removed in #98). HTTP 200, and the new footer was confirmed live. | Production-Verified | +| MCP Registry listing | Discover server | Registry | `server.json` | MCP Registry | `scripts/verify-mcp-registry.mjs` (run by `pnpm validate`) | see TESTS.md | Not shown | Registry lists `io.github.shmindmaster/gitpin` 0.6.4 as `isLatest: true`, status active (checked 2026-10-15) | Production-Verified for the listing; install from the registry not checked | +| npm package | Install `npx gitpin` | CLI | `package.json`, `scripts/verify-package.mjs`, `.github/workflows/release.yml` | npm | `src/launch-readiness-truth.test.ts` (version drift), `src/launch-readiness-task3.test.ts` | see TESTS.md | Not shown | `npm view gitpin version` returned 0.6.4, equal to main. `npx -y gitpin@0.6.4 gate` smoke passed (TESTS.md) | Production-Verified | + +## Integrations + +| Integration | Direction | Verification level | Evidence | +| --- | --- | --- | --- | +| npm (`gitpin`) | Publish; Action installs from it | Production-Verified: 0.6.4 installed with `npx` and run against this repository's history | `npm view gitpin version` | +| MCP Registry | Publish; clients discover | Listing checked: 0.6.4 latest, active. Client install via registry not checked | Registry v0.1 search for `io.github.shmindmaster/gitpin` | +| GitHub Pages | Deploy site | Production-Verified: deployed from `90598a1` (run 38737568718), HTTP 200, live content checked | `pages.yml` run plus a `curl` of the footer | +| GitHub Action | Consumer CI | Integrated: the repo's own evidence gate passed on PRs #111 and #113 | `action.yml`, `.github/workflows/evidence-gate.yml` | +| MCP clients (Claude Code, Codex, Cursor and others) | stdio and HTTP | Config templates validated by `pnpm verify:clients` (not run here). No live client session checked | `docs/clients.md`, `scripts/validate-client-configs.mjs` | +| GitHub rulesets and auto-merge | Required check, merge | Workflow exists; ruleset settings not checked | `.github/workflows/auto-merge-owner-prs.yml` | +| PostHog (site analytics) | Site to PostHog | Opt-out logic exists with a privacy spec; live capture not checked | `site/analytics.js`, `tests/browser/analytics-privacy.spec.mjs` | + +## Lists + +**UI without backend:** none found. The site is static and the product has no interactive UI. + +**Backend without UI:** all of it by design (CLI, Action, MCP). Two items have no demo: the HTTP transport and the MCP tools (the films cover the gate only). + +**Untested integrations (no executed test or live check in this pack):** +- GitHub Action as run on GitHub's hosted runners +- A real MCP client session over stdio or HTTP +- MCP Registry install path from a client +- A deployed HTTP instance +- Fresh `npx gitpin` install from npm + +**Works locally only (as far as checked):** +- `pnpm demo:gate` (synthetic repo in a scratch directory) +- HTTP MCP server (tests use a local server) +- Local snapshot index (`pnpm index:build`) +- The gate against a real repository other than GitPin's own: not checked + +**Pending, not evaluated:** PR #101 (faster git fixture tests, gate text output) and PR #102 (HTTP loopback by default, validate Origin). Both could change rows above once merged. diff --git a/docs/verification/DEMOS.md b/docs/verification/DEMOS.md new file mode 100644 index 0000000..8e65acb --- /dev/null +++ b/docs/verification/DEMOS.md @@ -0,0 +1,67 @@ +# Demos + +Every film below is a **real recording**. The real `gitpin` CLI runs in a real shell against fixture GP1, a synthetic git repository built by `scripts/build-pr-gate-fail-to-pass-artifact.mjs --persist --verify` (base `43439f8`, failing head `f0137d9`, passing head `57bce1a`, author `demo@gitpin.invalid`). Capture is headless Playwright (WebM) finished with Recast. There is no mock UI and no substitute footage. "Rosa" (release lead) is a fictional persona and is labelled as one on screen. + +Videos are kept outside the repository, in the owner's media library. SHA-256 hashes identify each exact file. + +## Delivered films + +| Film | Length | Recorded at | What it shows | QA on the exact file | SHA-256 | +|---|---|---|---|---|---| +| `Videos\Portfolio\GitPin\01 - The Gate That Wants Evidence.mp4` (+ `.srt`) | 1:35 | captured on the `03c1e69` build; every filmed command re-run byte-identical at main `e4c550e` | An agent PR changes `docs/protocol.md` and its manifest has `"claims": []`. `gitpin gate` returns `uncovered-change` and exit 1. One claim is added (line 5 plus `contentSha256`), and the same gate returns `evidence-verified` and exit 0. `git show … \| sha256sum` reproduces the hash. Shows the gate's coverage code in `src/gate.ts`, the CI signal (exit 1 fails, exit 0 clears), and the stated limit ("committed content, not semantic correctness"). | -16.0 LUFS; WER 0.0 (ElevenLabs scribe_v2 vs script); validate-encoded-picture PASS; story-experience review 90/100 | `d046bd6217fe7800c8d3b65ccc59caa8d0ab6c3ef417ce9870f3ffaa7f980012` | +| `Videos\Portfolio\GitPin\02 - The Gate in 30 Seconds.mp4` (+ `.srt`) | 0:32 | 1x frame-accurate cut of film 01; main still `e4c550e` on 2026-10-14 | Social cut with burned-in captions: zero claims → exit 1; one pinned claim → exit 0; plain-git recheck; the stated limit. | -16.0 LUFS; WER 0.0; no frozen picture ≥ 4 s; story-experience review 86/100 | `aff4b0f14fe89f226890f62e35e247aed53fea788d3fbb572a7209ee7fac8e7b` | +| `Videos\Evergreen\Proof Clips\02 - GitPin Evidence Gate.mp4` (+ `.srt`) | 0:13 | cut from film 01 | Fail, add claim, pass, plain-git recheck. Modular clip. | WER 0.0 | `2fd69c991c8897114fddfff604138965d3fd119f1dca883316889629acf82b75` | + +Narration: +- Film 01 uses ElevenLabs Eric. +- Film 02 and the proof clip use ElevenLabs Bella. +- Both run on `eleven_v4`, third person. + +Formats: +- Film 01 is format **B**, the functional walkthrough. +- Film 02 is a social teaser cut from it. +- Format **C**, the architecture and integration walkthrough, has not been made yet. + +## Traces behind the films +These are in the production workspace `D:\Local-AI\data\artifacts\media-studio\gitpin-hero-reel-20261014\`: +- `evidence/runtime-terminal-log.jsonl`: every command typed on camera, with output and exit code. +- `receipts/rerun-at-e4c550e.json`: the nine filmed commands re-run at `e4c550e` with byte-identical output. +- `receipts/gp1-verify-e4c550e.json`: the fixture verified at `e4c550e`. +- `receipts/claims-trace.json`: each spoken claim mapped to file:line. +- `qa/QA-RECORD.md`: the full gate table. + +The social cut's EDL, which records what each segment proves, is in `D:\Local-AI\data\artifacts\media-studio\gitpin-social30-20261014\story\edl.json`. + +## In-repo demo assets +- [`docs/demos/pr-gate-fail-to-pass.md`](../demos/pr-gate-fail-to-pass.md), with its `.artifact.json` and `.svg`. +- `pnpm demo:gate`, a live fail-to-pass run on a scratch synthetic repo (`scripts/demo-gate-hero.mjs`, covered by `src/gate-hero.test.ts`). + +## Not demonstrated +- An MCP client (Claude Code or another) calling `pin.search → pin.prove → pin.verify`. +- The GitHub Action failing a real agent PR in the Checks UI. +- The HTTP transport with a bearer token. + +## Production plan: the next two assets + +Both reuse the GP1 footage. Heavy work (capture, TTS, render) waits for an assigned slot. + +**C. Technical deep dive, "Inside the gate's trust boundary" (about 6 min, developer education).** Planned, not made. +- **Hook (0:00–0:15).** A PR tries to weaken its own gate by editing `.gitpin/gate.yml` in head. The gate reads policy from base, reports `policy-change-blocked`, and exits 1. +- **Axes (0:15–0:45).** Base versus head trust. Coverage from the merge-base diff, not from the PR description. A hash-pinned line versus a semantic check. +- **Behaviour, then diagram (0:45–3:15).** Reuse the GP1 fail and pass takes. Walk the 17-hop trace in [TECHNICAL.md](TECHNICAL.md) on the code stage: `gate.ts` policy block (55–67), line-slice hash (108–115), coverage (169–173). Show the Mermaid diagram after the behaviour. +- **MCP (3:15–4:15).** A real MCP client over stdio calling `pin.search` → `pin.prove` → `pin.verify`, ending with the evidence pack's full SHA and hash. +- **Trade-off (4:15–4:45).** "Committed content, not semantic correctness." The claim text is not judged, except through optional `mustContain`. +- **Failure and recovery (4:45–5:30).** Edit the cited line after proving: the gate reports `content-hash-mismatch`. Re-prove, and it passes. +- **Payoff (5:30–6:00).** The Action YAML and `npx -y gitpin@0.6.4`. One call to action. +- **New captures needed:** the policy-change block, the hash mismatch and the MCP session. +- **Voice:** the local Qwen3-TTS narrator (no ElevenLabs spend), with WER checked by local ASR. + +**Vertical short (≤ 45 s, 1080x1920).** Planned, not made. +- Reframed from film 02's beats: zero claims (exit 1), one pinned claim (exit 0), the plain-git recheck and the limit. +- Cropped from the GP1 Recast takes, not by letterboxing the 16:9 master. +- Burned-in captions. It reuses film 02's Bella lines, so there is no new TTS. + +**ElevenLabs usage to date:** +- Film 02 used 7 `eleven_v4` lines, 411 characters. The API's `character-cost` headers total 180. +- Its music bed was reused from film 01. +- Film 01's usage is recorded in its own job workspace. diff --git a/docs/verification/FUNCTIONAL.md b/docs/verification/FUNCTIONAL.md new file mode 100644 index 0000000..2df6b19 --- /dev/null +++ b/docs/verification/FUNCTIONAL.md @@ -0,0 +1,76 @@ +# GitPin Functional Verification + +Scope: GitPin 0.6.4. Read from source and docs at `e4c550e`. Test and smoke results are in [TESTS.md](TESTS.md). + +## Roles + +| Role | What they do | Permission model | +| --- | --- | --- | +| PR author or coding agent | Commits `.gitpin/change-evidence.json`, runs the gate locally | Git access to the repo only | +| Reviewer | Reads the report, rechecks locators with `git show` | Read access | +| CI owner | Installs `.gitpin/gate.yml` on the default branch, makes the check required | Repo admin. Policy change must be a separate merge | +| MCP client | Calls `pin.*` tools to find, prove and verify evidence | Local stdio: the process user. HTTP: bearer token in `GITPIN_MCP_TOKEN` | + +There are no user accounts, sessions or roles inside GitPin. Access control is Git access, the repo's rulesets, and the HTTP bearer token. + +## Journeys + +| # | Journey | Command or action | +| --- | --- | --- | +| 1 | Install policy (once, separate trusted change) | Copy `templates/gate.yml` to `.gitpin/gate.yml` on the default branch | +| 2 | Run the gate locally | `gitpin gate --base <40-char sha> --head <40-char sha> [--root ] [--policy ] [--repository ]` | +| 3 | Run the gate in CI | `uses: shmindmaster/gitpin@` with `base-sha` and `head-sha`. See [docs/pr-evidence-gate.md](../pr-evidence-gate.md) | +| 4 | Fix a failed gate | Read `violations[]` in the JSON report, add a claim or locator, recommit, rerun. The film shows `uncovered-change` fixed by one locator | +| 5 | Prove a claim | `pin.prove` (MCP) returns path, line, full SHA, `contentSha256` | +| 6 | Verify a claim | `gitpin verify --repository --path

--sha [--line ] [--must-contain ]`, or `--handle`, or `--from-pack` | +| 7 | Verify citations in a doc | `gitpin verify-cites --file notes.md` | +| 8 | Build a multi-cite set | `gitpin prove-set --from-json items.json` | +| 9 | Connect an MCP client | `gitpin init --client `, then `gitpin doctor` | +| 10 | Serve over HTTP | `pnpm mcp:http` or `node dist/http.js` with `GITPIN_MCP_TOKEN` set; POST `/api/mcp`; `GET /healthz` | +| 11 | Plain-git recheck | `git show :`, take the line range, SHA-256 it, compare to the locator | +| 12 | Hero demo | `pnpm demo:gate` builds a synthetic repo, fails with `uncovered-change`, adds one locator, passes | + +The twelve MCP tools: `pin.catalog`, `pin.search_docs`, `pin.search_code`, `pin.prove`, `pin.prove_set`, `pin.get_doc`, `pin.read`, `pin.verify`, `pin.verify_set`, `pin.analyze`, `pin.inspect`, `pin.compare`. Contracts: [docs/tools.md](../tools.md). There is no `pin.gate` tool: the gate is CLI and Action only. + +## Business rules (gate) + +- Policy is read with `git show :.gitpin/gate.yml`. The manifest is read with `git show :`. The working tree is never read. +- Both SHAs must be full 40-character hex. Short revisions are rejected. +- Required paths are the files in `git diff ..` that match `coverage.include` and not `coverage.exclude`, minus the manifest itself. +- Every required path must appear in some claim's `covers`, else `uncovered-change`. +- Each locator is a line range at `base` or `head`. The slice is the lines joined with `\n`, hashed with SHA-256, and must equal `contentSha256`. Max 200 lines per locator. +- `policyChanges: block` fails any PR that edits the policy file (`policy-change-blocked`). +- Limits in this repo's `.gitpin/gate.yml`: 100 changed paths, 50 claims, 8 locators per claim, 2 MiB per file. Schema maxima: 1000 paths, 100 claims, 16 locators, 10 MiB. +- A claim cannot cite the manifest (`manifest-self-citation`) or a sensitive path (`sensitive-evidence-path`; rules in `src/policy.ts`). +- Duplicate claim ids (`duplicate-claim-id`) and duplicate `covers` entries (`duplicate-cover`) fail. +- Deleted files are supported: cite them with `ref: base`. +- Git replace refs are ignored (`--no-replace-objects`), so a citation binds to the printed SHA. +- The gate runs no PR code and makes no network calls. + +## Errors and exit codes + +| Situation | Behaviour | +| --- | --- | +| All claims verified, all required paths covered | Report `status: ok`, exit 0 | +| Any violation | Report `status: failed`, exit 1, JSON report on stdout | +| Violation codes | `uncovered-change`, `content-hash-mismatch`, `evidence-unavailable`, `evidence-limit`, `duplicate-claim-id`, `duplicate-cover`, `policy-change-blocked`, `manifest-self-citation`, `sensitive-evidence-path` | +| Bad input or setup (short SHA, unknown option, missing or invalid policy, invalid manifest, over a limit, no merge base) | Exception, message on stderr, exit 1, no JSON report | +| `gitpin verify` and `verify-cites` | Exit 0 only when report status is `ok`; `missing`, `blocked`, `mismatch`, `contradicted` exit 1 | +| HTTP | 401 bad or missing token, 404 wrong path, 405 non-POST, 411/400/413 content-length problems, 421 host not allowed, 503 on `/healthz` when the registry is unusable | + +Recovery is by recommit: there is no state to repair. Setup errors and gate failures both exit 1, so a caller has to read stdout to tell them apart. + +## Data in and out + +In: Git objects, `.gitpin/gate.yml` (YAML), `.gitpin/change-evidence.json`, and for MCP an optional `repositories.yaml` registry. Out: JSON report (`kind: gitpin-gate-report`), citations, the Action step summary. Nothing is stored. See [TECHNICAL.md](TECHNICAL.md). + +## Known gaps + +- No semantic check that a claim's `statement` matches the cited lines. `gitpin verify` and `pin.verify` offer an optional `mustContain`; the gate does not use it. +- No SARIF output. On the roadmap ([ROADMAP.md](../../ROADMAP.md)). +- No signed or attested receipts; the report carries a `reportId` hash only. +- The HTTP transport is stateless-per-request in code but built the stateful-era way (`sessionIdGenerator: undefined`, SDK `^1.32.1`). Conformance with the 2026-07-28 MCP spec (`server/discover`, routing headers) is not evidenced. +- `gitpin gate` is not exposed as an MCP tool. +- Setup errors and gate failures share exit code 1. +- No published case of the gate catching a real agent PR (not checked beyond the repo's own docs). +- Open PRs, pending and not evaluated here: #101 (faster git fixture tests, gate text output) and #102 (HTTP binds loopback by default, validates Origin). Dependabot PRs are also open (#89, #95, #97, #103 to #109). diff --git a/docs/verification/READINESS.md b/docs/verification/READINESS.md new file mode 100644 index 0000000..4fa9234 --- /dev/null +++ b/docs/verification/READINESS.md @@ -0,0 +1,56 @@ +```yaml +product: GitPin +repo: C:\Repos\shmindmaster\gitpin (github.com/shmindmaster/gitpin) +commit: 90598a113345bcab83e4d06bc304d517c1076abe +verdict: Verified End-to-End Ready +functional_ready: yes +demo_ready: partial +production_ready: yes +deployed_sha: "npm gitpin 0.6.4 (release v0.6.4); MCP Registry 0.6.4; Pages at 90598a1" +tests: "vitest: 105/105 (18 files) x node22/node24 @ 90598a1 run 38736417735; site Playwright: 124/124 @ 90598a1; package + package-runtime node20/22/24: pass @ 90598a1; CodeQL: pass @ 90598a1; PR evidence gate: pass on #111, #113; prod smoke: npx gitpin@0.6.4 gate exit 0 (covered) and exit 1 (uncovered-change) on this repo 2026-10-15" +films: ["D:\\OneDrive - Maya Modest LLC\\Videos\\Portfolio\\GitPin\\01 - The Gate That Wants Evidence.mp4", "D:\\OneDrive - Maya Modest LLC\\Videos\\Portfolio\\GitPin\\02 - The Gate in 30 Seconds.mp4", "D:\\OneDrive - Maya Modest LLC\\Videos\\Evergreen\\Proof Clips\\02 - GitPin Evidence Gate.mp4"] +blockers: [] +next_action: "Produce the C technical deep dive (trust boundary, MCP prove/verify, hash-mismatch recovery) and the vertical short planned in DEMOS.md; then migrate the HTTP transport to MCP spec 2026-07-28 (stateless)." +updated: 2026-10-15T13:30:00Z +``` + +# Readiness + +**Verdict: Verified End-to-End Ready.** The scope is the PR evidence gate, the evidence prove and verify path, the CLI, the npm package, the Action in this repository, and the site. + +The core journey is proven at every rung: an agent PR claims nothing and fails, one line-hash claim is added, and it passes. +- It is tested on main's exact SHA (vitest on Node 22 and 24, the packed tarball on Node 20, 22 and 24). +- It is demonstrated in real recordings. +- It is production-verified: the npm `gitpin@0.6.4` gate ran against this repository's own history and produced both outcomes correctly. + +| Readiness | Rating | Why | +|---|---|---| +| Functional | **yes** | 105/105 vitest across 18 files at `90598a1`. The gate's coverage, policy-from-base, hash mismatch, deleted file and replace-refs cases are all covered. The repository's own PR evidence gate passed on #111 and #113. | +| Demo | **partial** | Film 01 (1:35), the 30 s social cut and the proof clip are real recordings, with QA in [DEMOS.md](DEMOS.md). The C deep dive and vertical short are planned, not made. An MCP client session and the Action failing a PR in the Checks UI are not filmed. | +| Production | **yes** | npm 0.6.4 and the MCP Registry listing 0.6.4 match main's version. The published gate was smoke-tested (exit 0 and exit 1 on real history). Pages was redeployed from `90598a1`, with the live content checked. | + +## Evidence ladder for the core capability (`gitpin gate --base --head`) + +| Rung | Evidence | +|---|---| +| Implemented | `src/gate.ts`, `src/gate-policy.ts`, `src/cli-gate.ts`, `src/cli-verify.ts` | +| Integrated | The 17-hop trace in [TECHNICAL.md](TECHNICAL.md), from the CLI argument to the exit code | +| Tested | [Validate 38736417735](https://github.com/shmindmaster/gitpin/actions/runs/38736417735) at `90598a1` (`src/gate.test.ts`, `src/gate-hero.test.ts`) | +| Demonstrated | `01 - The Gate That Wants Evidence.mp4`: exit 1 on `uncovered-change`, then exit 0 with `evidence-verified` | +| Production-Verified | `npx -y gitpin@0.6.4 gate` on this repository, 2026-10-15: exit 0 for a covered change, exit 1 with `uncovered-change` on exactly the 2 unclaimed paths | + +Per-capability rungs are in [COVERAGE.md](COVERAGE.md). + +## Fixed during this verification +- **Main was not being tested after auto-merge.** `GITHUB_TOKEN` pushes don't start workflows, so `e4c550e` and `56fcab9` had no Validate run. [#113](https://github.com/shmindmaster/gitpin/pull/113) adds `workflow_dispatch`, and `verify-ci-runner-routing.mjs` now requires it. +- **The live site was stale.** It still showed the company attribution removed in #98, because `pages.yml` only deploys on dispatch. It was redeployed from `90598a1` (run 38737568718), and the footer was verified live. + +## Risks and known limits (disclosed, not blockers) +- **The gate proves committed content, not semantic correctness.** Claim text is checked only through optional `mustContain`, and the gate's own message says so. +- **The MCP HTTP transport is stateful-era** (`sessionIdGenerator: undefined`, SDK ^1.32.1). Migrating to spec 2026-07-28 (stateless, `server/discover`) is not done. No deployed HTTP instance exists. +- **Roadmap items not shipped:** SARIF output, signed evidence receipts and release attestation verification. +- **Two root causes remain.** The auto-merge token's pushes don't start workflows, so main is tested by push only when a merge is made by hand, otherwise by dispatch. The site deploys only on manual dispatch. + +## Next highest-value action +1. Make the C deep dive "Inside the gate's trust boundary", then the vertical short ([DEMOS.md](DEMOS.md)), with local narration. +2. Migrate the MCP HTTP transport to spec 2026-07-28, then film a real MCP client running `pin.prove` and `pin.verify`. diff --git a/docs/verification/README.md b/docs/verification/README.md new file mode 100644 index 0000000..4286422 --- /dev/null +++ b/docs/verification/README.md @@ -0,0 +1,54 @@ +# GitPin Verification Pack + +Pack for GitPin 0.6.4. The code was read at `e4c550e`; the suites ran at main `90598a1`. This is evidence about the product, not marketing. Where a check was not run it says "not checked". + +## Purpose + +GitPin checks that what a pull request says it changed matches what the diff actually changed, and that every claim points at exact committed content. The check is deterministic Git and SHA-256. There is no model, database or index in the gate. + +Agents write PRs whose descriptions overstate the diff. GitPin makes a reviewer's job "re-run one `git show` and compare a hash" instead of "trust the description". + +## Users + +- PR authors and coding agents: commit a change-evidence manifest, run the gate locally. +- Reviewers: read the JSON report and recheck any locator with plain `git show`. +- CI owners and platform or security engineers: make the gate a required check. +- MCP clients (Claude Code, Codex, Cursor and others): call the `pin.*` tools to find, prove and verify evidence. + +## What makes it different + +- Policy comes from the base commit, so a PR cannot weaken its own gate. +- Coverage comes from the real merge-base diff, not from what the PR says it touched. +- Evidence is a line slice plus its SHA-256 at a full commit SHA, rechecked with `git show`, never from a working tree. +- Honest scope: it proves coverage, location, revision and hash. It does not prove the claim text is true. See [docs/pr-evidence-gate.md](../pr-evidence-gate.md). + +Existing assessment (reused, not rewritten): `reports\showcase\gitpin.md` in the AgentHub dispatch runtime folder (assessment dated 2026-10-14). + +## Capability inventory + +| Capability | Where | Notes | +| --- | --- | --- | +| PR evidence gate: `gitpin gate --base --head` | `src/gate.ts`, `src/gate-policy.ts`, `src/cli-gate.ts` | Exit 0 or 1, JSON report on stdout. [docs/pr-evidence-gate.md](../pr-evidence-gate.md) | +| 12 `pin.*` MCP tools over stdio | `src/server.ts`, `src/pin-tools.ts` | Read-only. [docs/tools.md](../tools.md) | +| Same tools over HTTP (`/api/mcp`, bearer token) | `src/http.ts` | [docs/remote-deployment.md](../remote-deployment.md) | +| Evidence prove and verify (CLI `verify`, `prove-set`, `verify-cites`) | `src/evidence-*.ts`, `src/cli-verify.ts` | [docs/cite-spec.md](../cite-spec.md) | +| Citations (`repo/path:line @ fullSha`, `gitpin:` handles) | `src/evidence-citation.ts` | | +| GitHub Action | `action.yml`, `scripts/render-gate-action-report.mjs` | Runs the published npm `gitpin` at a pinned version. [docs/ci.md](../ci.md) | +| Pages site | `site/`, `scripts/build-site.mjs`, `.github/workflows/pages.yml` | https://shmindmaster.github.io/gitpin/ | +| MCP Registry listing | `server.json`, `.github/workflows/publish-mcp.yml` | `io.github.shmindmaster/gitpin` | +| Hero fixture: `pnpm demo:gate` | `scripts/demo-gate-hero.mjs`, `src/gate-hero.test.ts` | Synthetic repo, fail then pass. [docs/demos/](../demos/) | +| Setup and health: `init`, `doctor`, `brief` | `src/onboarding.ts`, `src/doctor.ts`, `src/context-brief.ts` | Not covered in depth in this pack | + +## Pack contents + +- [FUNCTIONAL.md](FUNCTIONAL.md): roles, journeys, rules, errors, gaps. +- [TECHNICAL.md](TECHNICAL.md): architecture diagram, auth, CI/CD, and the line-level trace of `gitpin gate`. +- [COVERAGE.md](COVERAGE.md): capability-by-evidence matrix and integrations. +- [TESTS.md](TESTS.md): executed test commands and results (written separately). +- [DEMOS.md](DEMOS.md): film catalog (written separately). +- [READINESS.md](READINESS.md): verdict and status block (written separately). + +Delivered films, recorded at `e4c550e`: + +- `D:\OneDrive - Maya Modest LLC\Videos\Portfolio\GitPin\01 - The Gate That Wants Evidence.mp4` +- `D:\OneDrive - Maya Modest LLC\Videos\Portfolio\GitPin\02 - The Gate in 30 Seconds.mp4` diff --git a/docs/verification/TECHNICAL.md b/docs/verification/TECHNICAL.md new file mode 100644 index 0000000..9816b36 --- /dev/null +++ b/docs/verification/TECHNICAL.md @@ -0,0 +1,105 @@ +# GitPin Technical Verification + +Scope: GitPin 0.6.4. Read from source at `e4c550e`; the suites ran at `90598a1` (see [TESTS.md](TESTS.md)). Existing architecture docs: [docs/architecture.md](../architecture.md), [docs/architecture/overview.md](../architecture/overview.md). This file adds the gate trace and the verification view. + +## Architecture + +```mermaid +flowchart LR + subgraph Callers + CLI["gitpin gate (CLI)"] + ACT["GitHub Action (action.yml)"] + STDIO["MCP client, stdio"] + HTTP["MCP client, HTTP + bearer"] + end + ACT -->|npm exec gitpin@0.6.4| CLI + CLI --> CG["cli-gate.ts: parse args"] + CG --> G["gate.ts: runGitPinGate"] + G --> GP["gate-policy.ts"] + GP -->|git show base:.gitpin/gate.yml| POL["policy (trusted)"] + GP -->|git show head:manifest| MAN["manifest (submitted)"] + GP -->|git diff merge-base..head| DIFF["changed paths"] + G --> VER["per-locator slice + sha256 (inline in gate.ts)"] + POL --> G + MAN --> G + DIFF --> G + VER --> REP["JSON report + exit code"] + STDIO --> SRV["server.ts"] + HTTP --> H["http.ts: token, host, size checks"] --> SRV + SRV --> PT["pin-tools.ts (12 tools)"] + PT --> PROVE["evidence-prove.ts"] + PT --> VERIFY["evidence-verify.ts"] + PROVE --> GIT[("Git repos, read-only")] + VERIFY --> GIT + GP --> GIT +``` + +Single TypeScript package, CommonJS, Node 20 or later. Runtime dependencies: `@modelcontextprotocol/sdk`, `simple-git`, `yaml`, `zod`. No frontend app: `site/` is a static page (vanilla JS) built by `scripts/build-site.mjs`. + +## Data + +- No database, queue, cache or embeddings. Git is the only data store, read through `git show`, `git diff`, `git rev-parse`, `git merge-base`, `git grep` (`execFileSync`, no shell). +- Local files: `repositories.yaml` registry (`GITPIN_REGISTRY`), optional local snapshot index (`GITPIN_INDEX_PATH`, `pnpm index:build`; ignored by Git). The gate does not use either. +- Gate inputs are `.gitpin/gate.yml` and `.gitpin/change-evidence.json`, validated with zod (`src/gate-types.ts`). + +## Auth and secrets + +- Gate and stdio MCP: no auth; they run as the local user against local repos. +- HTTP MCP: `Authorization: Bearer` compared with `timingSafeEqual` against `GITPIN_MCP_TOKEN` (legacy alias `REPOCONTEXT_MCP_TOKEN`); the server refuses to start without it (`src/http.ts:15`). `GITPIN_ALLOWED_HOSTS` is a Host-header allowlist; `/healthz` skips it. Request size capped at 1 MiB, `content-length` required, no chunked bodies. Default bind is `0.0.0.0` on this commit; PR #102 changes that and is pending, not evaluated here. +- Sensitive paths are blocked at read time (`src/policy.ts`, `isAlwaysSensitivePath`). +- Other environment variable names: `GITPIN_REGISTRY`, `GITPIN_INDEX_PATH`, `HOST`, `PORT`, `GITPIN_MCP_URL`, `POSTHOG_GITPIN_PROJECT_KEY` (site analytics, with an opt-out in `site/analytics.js`). Values were not read. The Action passes `NPM_CONFIG_REGISTRY` and an empty npmrc so npm credentials are not used. +- Gate workflow is `pull_request` with `contents: read`. The docs say never `pull_request_target`. + +## CI/CD and release + +| Workflow | Trigger | What it does | +| --- | --- | --- | +| `ci.yml` | pull_request, push | `pnpm validate` (node matrix), package job (`verify:package`, `pnpm audit --audit-level high`, `npm pack`), website Playwright job | +| `evidence-gate.yml` | pull_request | Runs the GitPin Action on GitPin itself (self-test), pinned to the 0.6.4 release commit | +| `release.yml` | tag `v*.*.*`, manual | validate, build, `verify:package`, `verify:release-tag`, npm publish | +| `publish-mcp.yml` | manual only | Publishes to the MCP Registry via GitHub OIDC | +| `pages.yml` | manual only | Builds and deploys the site | +| `auto-merge-owner-prs.yml` | PR events | Squash-merge of owner PRs once required checks pass | + +Actions are pinned by commit SHA. `pnpm validate` chains lint, format check, typecheck, client config, CI routing, automerge test, env example, MCP registry, release tag, vitest, site build and the fail-to-pass artifact check. It passed on Node 22 and 24 at `90598a1` (Validate run 38736417735; see [TESTS.md](TESTS.md)). + +## Observability + +Stderr only: a ready line on server start and thrown error messages. HTTP has `GET/HEAD /healthz` (200 ready, 503 not ready). No metrics, tracing or structured logs. The gate's observable output is the JSON report (`reportId`, policy and manifest hashes) and the Action step summary. + +## Tech debt + +- Gate re-implements the line-slice hash inline (`gate.ts:108-115`) and does not call `evidence-verify.ts`; the two paths can drift. +- `evidence-verify.ts:110` has a no-op ternary (`headMatches ? 'supported' : 'supported'`). +- HTTP builds a new server and transport per request in the stateful-era style; MCP 2026-07-28 migration is not evidenced. +- Exit code 1 means both "gate failed" and "gate could not run". +- Some demo scripts name a local Qwen route (`record-demo-qwen.ps1`); not part of the product runtime. +- Roadmap items not shipped: threat model and abuse-case suite, release provenance, SARIF, OpenSSF Scorecard ([ROADMAP.md](../../ROADMAP.md)). + +## Critical-feature trace: `gitpin gate --base --head ` + +Line numbers are for `e4c550e`; none of the traced files changed through `90598a1`. No segment is simulated or a placeholder. + +| # | Hop | Where | What happens | +| --- | --- | --- | --- | +| 1 | Entry | `src/server.ts:37-43` | `process.argv[2]` is a command, so `runCli`; a thrown error prints to stderr and exits 1 | +| 2 | Dispatch | `src/cli.ts:49-53` | `command === 'gate'`: run, print `JSON.stringify(report, null, 2)`, set `process.exitCode = exitForVerify(report)` | +| 3 | Arg parsing | `src/cli-gate.ts:3-36` | Loops `--base --head --root --repository --policy`; unknown option or missing value throws; `--base` and `--head` required | +| 4 | Gate start | `src/gate.ts:24-34` | Resolves root, repository name, policy path; `isSafeRelativePath` rejects traversal and absolute paths | +| 5 | Resolve SHAs | `src/gate.ts:35-36`, `src/gate-policy.ts:6-15` | Requires 40 hex chars, then `git rev-parse --verify ^{commit}` | +| 6 | Merge-base | `src/gate.ts:37`, `src/gate-policy.ts:17-23` | `git merge-base base head` | +| 7 | Base policy | `src/gate.ts:38`, `src/gate-policy.ts:25-40` | `git show :.gitpin/gate.yml`, YAML parse, zod `gatePolicySchema` | +| 8 | Head manifest | `src/gate.ts:40-46`, `src/gate-policy.ts:42-58` | `git show :`, JSON parse, zod `changeEvidenceSchema`; claim count checked against policy limit | +| 9 | Merge-base diff | `src/gate.ts:48`, `src/gate-policy.ts:60-65` | `git diff --name-only --no-renames -z `; over the limit throws | +| 10 | Required paths and policy block | `src/gate.ts:55-67` | include/exclude globs (`gate.ts:219-244`), manifest excluded, `policy-change-blocked` if the policy file changed | +| 11 | Per-claim checks | `src/gate.ts:70-86` | `evidence-limit`, `duplicate-cover` | +| 12 | Locator guard | `src/gate.ts:95-105` | Manifest self-citation or sensitive path fails the locator | +| 13 | Per-claim line-slice hash | `src/gate.ts:108-115`, `src/gate-policy.ts:75-83` | `git show :`, split lines, reject range over 200 lines or past EOF, join slice with `\n`, SHA-256, compare to `contentSha256` (`content-hash-mismatch` on difference; `evidence-unavailable` on read error, `gate.ts:135-143`) | +| 14 | Citation | `src/gate.ts:124-133` | `buildCitation` with the full SHA for verified locators | +| 15 | Coverage check | `src/gate.ts:169-173` | Required paths not in any claim's `covers` become `uncovered-change` | +| 16 | JSON report | `src/gate.ts:175-206` | `status` is `ok` only with zero violations; `reportId` is a SHA-256 prefix of the report material | +| 17 | Exit code | `src/cli-verify.ts:20-22` | `status === 'ok'` gives 0, else 1 | + +In the Action, hop 1 is replaced by `npm exec --package=gitpin@0.6.4 -- gitpin gate ...` (`action.yml:65-73`), output goes to a report file, then `scripts/render-gate-action-report.mjs` renders it and the step exits with the gate's code. + +Executed: the published `gitpin@0.6.4` gate ran on this repository's own history on 2026-10-15. 56fcab9→df94cd3 gave `evidence-verified`, exit 0. 56fcab9→90598a1 gave `uncovered-change` on 2 paths, exit 1 (see [TESTS.md](TESTS.md)). The fail-to-pass run is `src/gate-hero.test.ts` and the films; see [TESTS.md](TESTS.md) and [DEMOS.md](DEMOS.md). diff --git a/docs/verification/TESTS.md b/docs/verification/TESTS.md new file mode 100644 index 0000000..90e591a --- /dev/null +++ b/docs/verification/TESTS.md @@ -0,0 +1,59 @@ +# Tests + +Every result below was executed and is linked to its run. Hosted runs are on ephemeral GitHub-hosted runners. Main is `90598a1` (#111), and the package is 0.6.4. + +## Main, at its exact SHA + +`Validate` ran on push to main for `90598a113345bcab83e4d06bc304d517c1076abe` on 2026-10-15: [run 38736417735](https://github.com/shmindmaster/gitpin/actions/runs/38736417735). All seven jobs passed. + +| Job | Commands (from `ci.yml`) | Environment | Result | +|---|---|---|---| +| validate (22) | `pnpm install --frozen-lockfile`, `pnpm validate`, `pnpm build` | ubuntu-latest, Node 22 | **pass**. Vitest: **105/105 tests in 18/18 files** | +| validate (24) | Same | Node 24 | **pass**. Vitest: **105/105 tests in 18/18 files** | +| website | `pnpm exec playwright install --with-deps chromium firefox webkit`, `pnpm site:test` | Chromium, Firefox, WebKit | **124 passed** | +| package | `pnpm build`, `pnpm verify:package`, `pnpm audit --audit-level high`, `npm pack` | Node 24, npm 12 | **pass** | +| package-runtime (20) | `node scripts/verify-package.mjs artifacts/*.tgz` (the packed tarball) | Node 20 | **pass** | +| package-runtime (22) | Same | Node 22 | **pass** | +| package-runtime (24) | Same | Node 24 | **pass** | + +`pnpm validate` chains the following: +- lint, format check and typecheck (Biome, `tsc`); +- client-config validation; +- the CI routing check (`verify-ci-runner-routing.mjs`, which since #113 also requires `workflow_dispatch`); +- the auto-merge controller test, the env example, the MCP Registry metadata and the release-tag check; +- vitest, the site build; +- the fail-to-pass artifact gate (`verify:artifact-gate-demo`). + +CodeQL also passed at `90598a1` (run 38736416516). + +## The repository's own PR evidence gate +`evidence-gate.yml` runs GitPin's gate on every PR against the base policy in `.gitpin/gate.yml`. It passed on: +- [#113](https://github.com/shmindmaster/gitpin/pull/113): one claim covering `ci.yml` and the routing script, with two line hashes. +- [#111](https://github.com/shmindmaster/gitpin/pull/111). + +## Production smoke checks (2026-10-15) + +| Check | Result | +|---|---| +| `npx -y gitpin@0.6.4 gate --base 56fcab9 --head df94cd3` on this repository (#113 alone, with its manifest) | `evidence-verified`, exit **0** | +| `npx -y gitpin@0.6.4 gate --base 56fcab9 --head 90598a1` (two PRs; only #111's manifest at head) | `failed`, `uncovered-change` on `.github/workflows/ci.yml` and `scripts/verify-ci-runner-routing.mjs`, exit **1**. These are exactly the two files #111 does not claim. | +| `npm view gitpin version` | 0.6.4 (release v0.6.4, 2026-09-16) | +| MCP Registry search `io.github.shmindmaster/gitpin` | 0.6.4 listed | +| Pages | Redeployed from `90598a1` ([run 38737568718](https://github.com/shmindmaster/gitpin/actions/runs/38737568718)); HTTP 200; the live footer now reads "independent open-source maintainer" | + +## Local runs + +| Command | Commit | Result | +|---|---|---| +| `node scripts/verify-ci-runner-routing.mjs` against main's old `ci.yml` and against the #113 change (Windows, Node 26) | #113 branch | fails without `workflow_dispatch`; passes with it | +| `node dist/server.js gate --base origin/main --head HEAD` (local build) on the #113 branch | #113 branch | `ok`, exit 0 | +| The films' terminal runs (9 commands on fixture GP1) | captured on `03c1e69`, re-run byte-identical at `e4c550e` | see [DEMOS.md](DEMOS.md) | + +## Not covered by any executed run +- A real MCP client session over stdio or HTTP. The tools are covered by vitest, not by an end-to-end client. +- The HTTP transport on a deployed host. Only local tests (`src/http.test.ts`) cover it. +- The Action in a third-party consumer repository. Only this repository's own evidence gate exercises it. + +## Gaps found and fixed while assembling this file +- **Main was not tested after auto-merge.** Pushes made with `GITHUB_TOKEN` don't start workflows, so `e4c550e` and `56fcab9` had no Validate run of their own. [#113](https://github.com/shmindmaster/gitpin/pull/113) adds `workflow_dispatch`, and the routing check now enforces it. +- **The live site was stale.** `pages.yml` deploys only on manual dispatch, so the site still showed the company attribution that #98 removed. It was redeployed from `90598a1` and checked live.