diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dc28115 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +All notable changes to GraphKeeper are documented here. GraphKeeper follows semantic +versioning; while the package is below 1.0, minor releases may change public behavior. + +## [0.1.3] - 2026-08-11 + +### Added + +- Canonical npm repository, homepage, issue-tracker, and discovery metadata. +- README installation guidance, package/CI status badges, and public support and + security-reporting paths. +- Contributor architecture guidance covering command flow, extension points, + platform boundaries, recovery, and release governance. +- Parser-parity and documentation-contract tests that keep the TypeScript readers, + shell validator, and contributor guidance aligned. + +### Fixed + +- Preserve LF line endings for shell assets packaged from Windows so they remain + executable in supported POSIX shells. +- Canonicalize temporary repository paths in cross-platform tests. +- Run documented contributor onboarding from a clean npm installation in WSL instead + of linking Windows-installed dependencies. +- Serialize package end-to-end tests so concurrent `npm pack` builds cannot rewrite + shared output during verification. + +### Changed + +- The canonical default branch is `main`, protected by required Linux, macOS, and + Windows/Git Bash quality checks plus isolated performance checks. +- Repository-local planning artifacts and development-only `.codex` state are excluded + from version control and npm packages. +- Solo-maintainer pull requests require passing checks and manual merge but no separate + approval; the policy calls for peer approval when another trusted maintainer gains + write access. diff --git a/README.md b/README.md index e012728..18636c8 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ When linear JSON scans or concurrent-write collisions become material, a future See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development workflow, extension points, and boundaries, [`SUPPORT.md`](.github/SUPPORT.md) for usage help, and [`SECURITY.md`](.github/SECURITY.md) for private vulnerability reporting. Version -`0.1.2` is pre-1.0 and its API may change. Release ownership and the target version +`0.1.3` is pre-1.0 and its API may change. Release ownership and the target version must be verified immediately before publishing; completing the repository release gates does not publish anything. diff --git a/package-lock.json b/package-lock.json index 8142cd5..5728caa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "graphkeeper", - "version": "0.1.2", + "version": "0.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "graphkeeper", - "version": "0.1.2", + "version": "0.1.3", "license": "MIT", "bin": { "graphkeeper": "dist/src/cli.js" diff --git a/package.json b/package.json index e43dbbc..34119d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphkeeper", - "version": "0.1.2", + "version": "0.1.3", "description": "Grounded, auditable memory for coding agents.", "homepage": "https://github.com/anusbutt/Graph_Keeper#readme", "bugs": { @@ -20,13 +20,14 @@ } }, "bin": { - "graphkeeper": "./dist/src/cli.js" + "graphkeeper": "dist/src/cli.js" }, "files": [ "dist/src", "scripts/validate.sh", "templates", "examples", + "CHANGELOG.md", "LICENSE", "README.md" ], diff --git a/src/cli.ts b/src/cli.ts index a134b06..3829719 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -26,7 +26,7 @@ export interface CliIO { readonly stderr: (message: string) => void; } -const VERSION = '0.1.2'; +const VERSION = '0.1.3'; const COMMANDS = new Set(['init', 'check', 'query', 'doctor', 'update']); const USAGE = [ diff --git a/tests/e2e/cli-help.test.ts b/tests/e2e/cli-help.test.ts index f1064d3..2bbdc11 100644 --- a/tests/e2e/cli-help.test.ts +++ b/tests/e2e/cli-help.test.ts @@ -75,7 +75,7 @@ test('prints the package version successfully', async () => { const exitCode = await run(['--version'], capture.io); assert.equal(exitCode, EXIT_SUCCESS); - assert.deepEqual(capture.stdout, ['0.1.2']); + assert.deepEqual(capture.stdout, ['0.1.3']); assert.equal(capture.stderr.length, 0); }); @@ -118,7 +118,7 @@ test('compiled CLI entrypoint runs through an npm-style symlink', { const result = await runCommand(process.execPath, [linkPath, '--version'], directory); assert.equal(result.exitCode, EXIT_SUCCESS); - assert.equal(result.stdout, '0.1.2\n'); + assert.equal(result.stdout, '0.1.3\n'); assert.equal(result.stderr, ''); } finally { await rm(directory, { recursive: true, force: true }); diff --git a/tests/e2e/contributor-onboarding.test.ts b/tests/e2e/contributor-onboarding.test.ts index 7532440..e7fee02 100644 --- a/tests/e2e/contributor-onboarding.test.ts +++ b/tests/e2e/contributor-onboarding.test.ts @@ -63,6 +63,10 @@ test('a clean source snapshot completes onboarding gates and a query recipe with GRAPHKEEPER_ONBOARDING_NESTED: '1', }; delete nestedEnvironment.NODE_TEST_CONTEXT; + for (const name of Object.keys(nestedEnvironment)) { + if (name.toLowerCase() === 'npm_config_dry_run') delete nestedEnvironment[name]; + } + nestedEnvironment.npm_config_dry_run = 'false'; const install = npmInvocation(['ci']); const installed = await runProcess(install.command, install.args, { cwd: fixture.root, diff --git a/tests/e2e/package-contents.test.ts b/tests/e2e/package-contents.test.ts index c9405ba..99635e4 100644 --- a/tests/e2e/package-contents.test.ts +++ b/tests/e2e/package-contents.test.ts @@ -30,7 +30,13 @@ test('release tarball contains every runtime asset and excludes development-only }, async (t) => { const temporary = await mkdtemp(join(tmpdir(), 'graphkeeper-pack-')); t.after(() => rm(temporary, { recursive: true, force: true })); - const invocation = npmInvocation(['pack', '--json', '--pack-destination', temporary]); + const invocation = npmInvocation([ + 'pack', + '--dry-run=false', + '--json', + '--pack-destination', + temporary, + ]); const packed = await runProcess(invocation.command, invocation.args, { cwd: projectRoot, timeoutMs: 45_000, @@ -51,6 +57,7 @@ test('release tarball contains every runtime asset and excludes development-only 'examples/reviewer.md', 'examples/worked-example/graph/claims.json', 'examples/worked-example/evidence/initial-failure.log', + 'CHANGELOG.md', 'LICENSE', 'README.md', ]) { diff --git a/tests/e2e/package-install.test.ts b/tests/e2e/package-install.test.ts index 763bcc7..8dc12c6 100644 --- a/tests/e2e/package-install.test.ts +++ b/tests/e2e/package-install.test.ts @@ -53,7 +53,13 @@ test('a tarball installs in a clean directory and runs init, check, query, and d await rm(installationRoot, { recursive: true, force: true }); }); - const packInvocation = npmInvocation(['pack', '--json', '--pack-destination', packingRoot]); + const packInvocation = npmInvocation([ + 'pack', + '--dry-run=false', + '--json', + '--pack-destination', + packingRoot, + ]); const packed = await runProcess(packInvocation.command, packInvocation.args, { cwd: projectRoot, timeoutMs: 45_000, @@ -66,6 +72,7 @@ test('a tarball installs in a clean directory and runs init, check, query, and d const installInvocation = npmInvocation([ 'install', + '--dry-run=false', '--ignore-scripts', '--no-audit', '--no-fund', @@ -91,6 +98,9 @@ test('a tarball installs in a clean directory and runs init, check, query, and d const help = await runCli(['--help']); assert.equal(help.exitCode, 0, help.stderr); assert.match(help.stdout, /graphkeeper update/); + const version = await runCli(['--version']); + assert.equal(version.exitCode, 0, version.stderr); + assert.equal(version.stdout, '0.1.3\n'); const initialized = await runCli(['init']); assert.equal(initialized.exitCode, 0, initialized.stderr); assert.match(initialized.stdout, /CREATE graph\/entities\.json/); diff --git a/tests/integration/release-docs.test.ts b/tests/integration/release-docs.test.ts index 7665dba..8185dea 100644 --- a/tests/integration/release-docs.test.ts +++ b/tests/integration/release-docs.test.ts @@ -34,6 +34,7 @@ test('npm metadata points to the canonical public repository and support channel homepage?: string; bugs?: { url?: string }; repository?: { type?: string; url?: string }; + bin?: Record; keywords?: string[]; }; @@ -46,11 +47,33 @@ test('npm metadata points to the canonical public repository and support channel type: 'git', url: 'git+https://github.com/anusbutt/Graph_Keeper.git', }); + assert.deepEqual(manifest.bin, { graphkeeper: 'dist/src/cli.js' }); for (const keyword of ['ai-agents', 'coding-agents', 'knowledge-graph', 'provenance']) { assert.ok(manifest.keywords?.includes(keyword), `missing npm keyword: ${keyword}`); } }); +test('release version stays aligned across package, lockfile, CLI, README, and changelog', async () => { + const manifest = JSON.parse(await readFile(join(projectRoot, 'package.json'), 'utf8')) as { + version?: string; + }; + const lockfile = JSON.parse(await readFile(join(projectRoot, 'package-lock.json'), 'utf8')) as { + version?: string; + packages?: Record; + }; + const cli = await readFile(join(projectRoot, 'src/cli.ts'), 'utf8'); + const readme = await readFile(join(projectRoot, 'README.md'), 'utf8'); + const changelog = await readFile(join(projectRoot, 'CHANGELOG.md'), 'utf8'); + const version = manifest.version; + + assert.equal(version, '0.1.3'); + assert.equal(lockfile.version, version); + assert.equal(lockfile.packages?.['']?.version, version); + assert.match(cli, new RegExp("const VERSION = '" + version?.replaceAll('.', '\\.') + "';")); + assert.match(readme, new RegExp('Version\\s+`' + version?.replaceAll('.', '\\.') + '`')); + assert.match(changelog, new RegExp('## \\[' + version?.replaceAll('.', '\\.') + '\\] - 2026-08-11')); +}); + test('release carries the MIT terms for GraphKeeper contributors', async () => { const license = await readFile(join(projectRoot, 'LICENSE'), 'utf8'); assert.match(license, /^MIT License/);