5pm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
1
+ {
2
+ "result": "passed",
3
+ "version": "0.1.0",
4
+ "checked_at": "2026-10-30T18:54:14Z",
5
+ "evidence_commit": "8c23afad10bb7c66166012f9c6dff669d26972cf",
6
+ "worktree_dirty": true,
7
+ "source_sha256": "78dc8399633fc1f848b5cde1fc86f5a2b5b562a54bb921318f5cfc1583522459",
8
+ "disposable_tenant": true,
9
+ "mutations_enabled": true,
10
+ "release_evidence": false,
11
+ "api_operations_verified": [
12
+ "activity.add",
13
+ "activity.attachFiles",
14
+ "activity.downloadFile",
15
+ "activity.getById",
16
+ "activity.getFilesList",
17
+ "activity.getList",
18
+ "activity.remove",
19
+ "activity.removeFile",
20
+ "activity.update",
21
+ "authentication.getAuthorizedUser",
22
+ "authentication.isSsl",
23
+ "authentication.nop",
24
+ "authentication.signIn",
25
+ "authentication.signOut",
26
+ "projects.add",
27
+ "projects.getAll",
28
+ "projects.getById",
29
+ "projects.getList",
30
+ "projects.remove",
31
+ "projects.update",
32
+ "projectsgroups.add",
33
+ "projectsgroups.getAll",
34
+ "projectsgroups.getById",
35
+ "projectsgroups.remove",
36
+ "projectsgroups.update",
37
+ "tasks.add",
38
+ "tasks.getById",
39
+ "tasks.getList",
40
+ "tasks.remove",
41
+ "tasks.update",
42
+ "users.add",
43
+ "users.getAll",
44
+ "users.getAuthorizedUser",
45
+ "users.getByEmail",
46
+ "users.getById",
47
+ "users.remove",
48
+ "users.update"
49
+ ],
50
+ "api_operations_missing": [],
51
+ "reports": [
52
+ {
53
+ "check": "live_reads",
54
+ "passed": true,
55
+ "report": {
56
+ "result": "passed",
57
+ "checks": [
58
+ "contract.authentication",
59
+ "contract.projects",
60
+ "contract.tasks",
61
+ "contract.projectsgroups",
62
+ "contract.activity",
63
+ "contract.users",
64
+ "contract.schema",
65
+ "authentication.authorized_user",
66
+ "authentication.nop",
67
+ "authentication.isSsl",
68
+ "projects.list",
69
+ "projects.get",
70
+ "projects.pagination",
71
+ "project_groups.list",
72
+ "tasks.list",
73
+ "tasks.get",
74
+ "tasks.pagination",
75
+ "activity.list",
76
+ "activity.get",
77
+ "activity.pagination",
78
+ "users.list",
79
+ "users.get",
80
+ "projects.getAll",
81
+ "users.getAuthorizedUser",
82
+ "users.find_by_email",
83
+ "activity.files",
84
+ "mcp.stdio",
85
+ "cleanup.no_disposable_records"
86
+ ],
87
+ "mutations_tested": false
88
+ },
89
+ "diagnostics": []
90
+ },
91
+ {
92
+ "check": "live_mutations",
93
+ "passed": true,
94
+ "report": {
95
+ "result": "passed",
96
+ "checks": [
97
+ "project_groups.create",
98
+ "project_groups.update",
99
+ "projects.create",
100
+ "projects.update",
101
+ "tasks.create",
102
+ "tasks.update",
103
+ "activity.create",
104
+ "activity.update",
105
+ "project_groups.get",
106
+ "projects.get",
107
+ "tasks.get",
108
+ "activity.get",
109
+ "mcp.all_read_tools",
110
+ "activity.delete",
111
+ "tasks.delete",
112
+ "projects.delete",
113
+ "project_groups.delete",
114
+ "mcp.approval",
115
+ "mcp.projects.create",
116
+ "mcp.tasks.create",
117
+ "mcp.projects.update",
118
+ "mcp.projects.get",
119
+ "mcp.tasks.update",
120
+ "mcp.tasks.get",
121
+ "mcp.tasks.delete_denied",
122
+ "mcp.tasks.delete",
123
+ "mcp.projects.delete_denied",
124
+ "mcp.projects.delete",
125
+ "mcp.audit"
126
+ ],
127
+ "mcp_tools_verified": [
128
+ "five_pm_create_projects",
129
+ "five_pm_create_tasks",
130
+ "five_pm_delete_projects",
131
+ "five_pm_delete_tasks",
132
+ "five_pm_get_activity",
133
+ "five_pm_get_project_groups",
134
+ "five_pm_get_projects",
135
+ "five_pm_get_tasks",
136
+ "five_pm_get_users",
137
+ "five_pm_list_activity",
138
+ "five_pm_list_project_groups",
139
+ "five_pm_list_projects",
140
+ "five_pm_list_tasks",
141
+ "five_pm_list_users",
142
+ "five_pm_update_projects",
143
+ "five_pm_update_tasks"
144
+ ],
145
+ "cleanup": true
146
+ },
147
+ "diagnostics": []
148
+ },
149
+ {
150
+ "check": "live_attachments_users",
151
+ "passed": true,
152
+ "report": {
153
+ "result": "passed",
154
+ "checks": [
155
+ "users.create",
156
+ "users.update",
157
+ "users.get",
158
+ "users.delete",
159
+ "activity.attach_files",
160
+ "activity.files",
161
+ "activity.download_file",
162
+ "activity.delete_file"
163
+ ]
164
+ },
165
+ "diagnostics": []
166
+ },
167
+ {
168
+ "check": "live_cleanup",
169
+ "passed": true,
170
+ "report": {
171
+ "result": "passed",
172
+ "cleanup_verified": true
173
+ },
174
+ "diagnostics": []
175
+ }
176
+ ]
177
+ }
@@ -0,0 +1,109 @@
1
+ # Disposable-account validation
2
+
3
+ Live validation is optional and runs only when Stan requests it. It is not a release
4
+ or CI prerequisite and requires no ongoing subscription. Historical results remain
5
+ valid records of their original runs. See [release policy](releasing.md) and
6
+ [cached validation](cached-validation.md) for owner-controlled refreshes.
7
+
8
+ Live validation on 2026-10-30 exercised both packages using a disposable tenant.
9
+ Reports and fixtures exclude its hostname, credentials, session tokens, record IDs,
10
+ account payloads and attachment contents.
11
+
12
+ ## Qualification prerequisites
13
+
14
+ Before live qualification, independently verify the designated disposable tenant is
15
+ active, isolated from production, and contains no business records requiring
16
+ preservation. Verify the credential is Cora's standard User, not Admin. Owner
17
+ confirmation alone does not replace these read-only checks. Do not change roles,
18
+ create replacement users or elevate privileges without separate authorization.
19
+
20
+ Run the sandbox preflight first, then compare the tenant's six WSDL inventories and
21
+ XSD against the snapshots before running the documented bounded mutation suite.
22
+ Operate only on newly created labelled records; verify attachment removal, deletion
23
+ readbacks, zero labelled leftovers and session cleanup. Stop on unverified isolation,
24
+ permissions or cleanup. Record failures and the exact tested commit without secrets
25
+ or account payloads. Live testing remains opt-in and is never part of CI.
26
+
27
+ On 2026-10-30 qualification stopped before the sandbox/contract/mutation suite:
28
+ the supplied test credential was Admin, the saved browser session was signed out,
29
+ and independent isolation/record inventory could not be completed. Read-only
30
+ role-verification sessions were closed. No new qualification success is claimed.
31
+ A securely supplied standard User account and independent tenant verification are
32
+ required to resume. The earlier historical report remains development evidence.
33
+
34
+ ## Reproduce
35
+
36
+ Inject `FIVE_PM_HOST`, `FIVE_PM_LOGIN` and `FIVE_PM_PASSWORD` from an environment or
37
+ secret manager. Set `FIVE_PM_SANDBOX=1` and `FIVE_PM_SANDBOX_HOST` to the same host.
38
+ Run `bundle exec ruby bin/live-check` for read-only validation. Authentication creates
39
+ temporary API sessions, which the suite closes.
40
+
41
+ For explicitly authorized disposable CRUD checks, also set
42
+ `FIVE_PM_ALLOW_MUTATIONS=1` and run `bundle exec ruby bin/live-check --mutations`.
43
+ This creates labelled temporary records, a reserved-domain test user, and a tiny
44
+ synthetic text attachment. It deletes those records, checks the attachment's exact
45
+ bytes, verifies missing-record errors and confirms no labelled records remain.
46
+ Never enable this against a production tenant. Live tests are not part of CI.
47
+
48
+ The single JSON report identifies the base commit, dirty-worktree status and a
49
+ SHA-256 digest of the tested source. The digest covers the sorted Git tracked and
50
+ untracked, non-ignored files as `path`, NUL, byte length, NUL, then file bytes;
51
+ `docs/live-validation-report.json` itself is excluded. It is development evidence,
52
+ not a fresh validation of a different source commit. `bin/release-check` does not require live evidence.
53
+
54
+ The harness records successful transport operation names only, including calls made
55
+ by the actual stdio process. With `--mutations`, it fails unless every one of the
56
+ 37 inventoried SDK operations was exercised. The report lists both verified and
57
+ missing operations. It also requires a successful live call to every one of the
58
+ 16 tools advertised by the mutation-enabled MCP server (10 reads, six writes).
59
+ No arguments, record IDs, return payloads or credentials enter this operation audit.
60
+
61
+ The web UI normally shows only the original demo data after verification because
62
+ all labelled test records are deleted. Write verification includes read-after-update
63
+ assertions, post-delete failures and a final scan for leftover records; it does not
64
+ depend on retaining records in the UI.
65
+ Attachment removal requires a successful deletion result and a new attachment-list
66
+ request showing the target file is absent before parent-record cleanup begins.
67
+ Subprocess deadlines cover process exit and pipe draining; timed-out process groups
68
+ are killed and reaped rather than waiting indefinitely in Open3 cleanup.
69
+ Cleanup entries remain tracked until both a successful deletion result and absence
70
+ are verified. False results and failed readbacks preserve entries for recovery.
71
+ Users/groups use generic API errors for missing records, so their complete getAll
72
+ lists must independently confirm absence. Cleanup first checks whether an earlier
73
+ deletion already succeeded before sending another deletion request.
74
+
75
+ ## Verified scope
76
+
77
+ - Six account-specific WSDL POST operation inventories and the XSD model inventory
78
+ match the public snapshots.
79
+ - Sign-in/out, authorized-user readers, SSL check, no-op, all resource lists and
80
+ individual reads, email lookup, and bounded offset pagination.
81
+ - Create/update/get/delete of disposable projects, tasks, project groups, activity
82
+ and a user; the test user uses a reserved `.invalid` email address.
83
+ - Attachment upload with explicit base64 content, metadata listing, bounded binary
84
+ download with byte-for-byte verification, and deletion.
85
+ - Actual MCP stdio initialization, discovery, all five list tools and default write
86
+ denial; embedded project/task creation, update, get and deletion with exact per-call
87
+ approval, denied reuse, rejected agent booleans and redacted audit fields.
88
+ - Offline schema/protocol/security regressions, package installation checks and
89
+ the supported Ruby 3.2/3.3/3.4/4.0 matrix.
90
+
91
+ ## Observed differences from the WSDL
92
+
93
+ - JSON results use `data` instead of WSDL response-part names. Reader views map
94
+ these to the operation's declared part; raw JSON is retained unchanged.
95
+ - `User.canViewProfiles` is a JSON boolean despite the XSD string declaration.
96
+ Both strings and booleans are retained in their original representation.
97
+ - Project/task deletion returns an integer marker; group/activity deletion returns
98
+ a boolean. Integer markers are preserved without assigning undocumented meaning.
99
+ - API errors can arrive with HTTP 200 and numeric `code` plus `message`, including
100
+ code 0 for missing required parameters and code 441 for missing records. Only
101
+ the numeric code is exposed; remote messages are discarded.
102
+ - User creation accepts symbolic role names (the suite uses `user`) and rejects
103
+ the numeric role representations shown in the help reference.
104
+ - `nop` returns `data: null`. File downloads return binary bytes despite JSON options.
105
+
106
+ Dates remain exact strings; no timezone inference or conversion is performed.
107
+ Retries, rate limits, transport failures and malformed schemas are tested with
108
+ synthetic responses rather than inducing failures or load on the live account.
109
+ Publication, hosted-CI acceptance and accepted-commit release evidence remain separate gates.
data/docs/mcp-plan.md ADDED
@@ -0,0 +1,19 @@
1
+ # Standalone MCP adapter status
2
+
3
+ Tracks [issue #4](https://github.com/scarver2/5pm-ruby/issues/4).
4
+ The separately packaged implementation lives in `mcp/`; see its README for setup,
5
+ security boundaries, supported tools and test commands.
6
+
7
+ Implemented with synthetic regression tests and disposable-account checks:
8
+
9
+ - Official MCP Ruby SDK for protocol handling; no MCP dependency in the core gem.
10
+ - Read-only stdio executable and allowlisted resource tools.
11
+ - Optional embedded task/project mutations require explicit enablement and a trusted
12
+ per-request approval callback over immutable exact arguments.
13
+ - Redacted responses/errors and audit events, bounded frames/pages, and no URL fetching.
14
+ - Real stdio integration tests, executable tests, and isolated installed-package checks.
15
+ - Generic host setup usable by Rodeo/A1Web without integration-specific dependencies.
16
+
17
+ Live verification also covers the actual stdio executable and project/task mutation
18
+ tools, including fresh operator approval and denied calls. See [live validation](live-validation.md).
19
+ Remaining: source review, accepted-commit release evidence, and explicit approval before publication.
@@ -0,0 +1,112 @@
1
+ {
2
+ "status": "preparation_only_blocked",
3
+ "publication_authorized": false,
4
+ "proposed_version": "0.1.0",
5
+ "source_commit": "9322e03f75604d7279775dc88c9192add840e76e",
6
+ "source_date_epoch": 1793399403,
7
+ "source_worktree_clean": true,
8
+ "changelog_state": "0.1.0 \u2014 unreleased",
9
+ "runtime": {
10
+ "ruby": "4.0.9",
11
+ "rubygems": "4.0.23",
12
+ "bundler": "4.0.23"
13
+ },
14
+ "supported_ruby": [
15
+ "3.2",
16
+ "3.3",
17
+ "3.4",
18
+ "4.0"
19
+ ],
20
+ "source_ci_run": 41138526407,
21
+ "source_ci_conclusion": "success",
22
+ "independent_rebuild_byte_identical": true,
23
+ "isolated_install_and_load": "passed",
24
+ "names_public_api_status": {
25
+ "5pm": 404,
26
+ "5pm-mcp": 404
27
+ },
28
+ "manual_authentication": "Sheriff existing local credentials only; no Cora access or token issuance; actual account/key scopes unverified",
29
+ "blockers": [
30
+ "One leftover labelled User requires owner cleanup and independent read-only verification",
31
+ "users.update API code 8; attachments not qualified",
32
+ "Required exact-source docs/live-validation.json absent/incomplete",
33
+ "Changelog still unreleased; dated final source must be rebuilt and requalified",
34
+ "Final exact artifact/candidate acceptance and separate Sheriff push authorization absent"
35
+ ],
36
+ "artifacts": [
37
+ {
38
+ "name": "5pm",
39
+ "file": "5pm-0.1.0.gem",
40
+ "sha256": "160b09db75dd1cf0aebac21e64f0f6a1216ea437c3155816481a1c7f0faff0e6",
41
+ "files": [
42
+ "CHANGELOG.md",
43
+ "LICENSE",
44
+ "README.md",
45
+ "SECURITY.md",
46
+ "docs/api-support.md",
47
+ "docs/implementation-guides.md",
48
+ "docs/implementation-status.md",
49
+ "docs/live-validation-report.json",
50
+ "docs/live-validation.md",
51
+ "docs/mcp-plan.md",
52
+ "docs/releasing.md",
53
+ "examples/read_only.rb",
54
+ "lib/five_pm.rb",
55
+ "lib/five_pm/client.rb",
56
+ "lib/five_pm/download.rb",
57
+ "lib/five_pm/errors.rb",
58
+ "lib/five_pm/operations.json",
59
+ "lib/five_pm/pagination.rb",
60
+ "lib/five_pm/request.rb",
61
+ "lib/five_pm/resource.rb",
62
+ "lib/five_pm/response.rb",
63
+ "lib/five_pm/retry_policy.rb",
64
+ "lib/five_pm/services.rb",
65
+ "lib/five_pm/transport.rb",
66
+ "lib/five_pm/types.json",
67
+ "lib/five_pm/version.rb",
68
+ "sig/five_pm.rbs",
69
+ "sig/models.rbs"
70
+ ],
71
+ "dependencies": [
72
+ {
73
+ "name": "json",
74
+ "requirement": ">= 2.6, < 4"
75
+ },
76
+ {
77
+ "name": "net-http",
78
+ "requirement": ">= 0.3, < 1"
79
+ }
80
+ ],
81
+ "license": "MIT",
82
+ "required_ruby": ">= 3.2"
83
+ },
84
+ {
85
+ "name": "5pm-mcp",
86
+ "file": "5pm-mcp-0.1.0.gem",
87
+ "sha256": "8ac7c8a5dc175b7ecd4d934b3c720ee3d4b0f019deacd3c5e94d249a3e22d601",
88
+ "files": [
89
+ "LICENSE",
90
+ "README.md",
91
+ "exe/five-pm-mcp",
92
+ "lib/five_pm/mcp.rb",
93
+ "lib/five_pm/mcp/server.rb",
94
+ "lib/five_pm/mcp/version.rb",
95
+ "sig/five_pm-mcp.rbs",
96
+ "sig/manifest.yaml"
97
+ ],
98
+ "dependencies": [
99
+ {
100
+ "name": "5pm",
101
+ "requirement": "~> 0.1.0"
102
+ },
103
+ {
104
+ "name": "mcp",
105
+ "requirement": "~> 1.6.1"
106
+ }
107
+ ],
108
+ "license": "MIT",
109
+ "required_ruby": ">= 3.2"
110
+ }
111
+ ]
112
+ }
@@ -0,0 +1,28 @@
1
+ # Historical limited-release proposal — NOT APPROVED
2
+
3
+ This proposal predates Stan's owner-controlled live-validation policy. It does not
4
+ impose a current live qualification gate. No API exclusions below are implemented.
5
+ See [current release policy](../releasing.md). The original proposal follows.
6
+
7
+ Default recommendation: HOLD until current qualification gates pass. If Sheriff and
8
+ Deputy instead want an intentionally limited first release, review this as a separate
9
+ scope/policy proposal before code or gate changes:
10
+
11
+ - Exclude User mutations (`users.add`, `users.update`, `users.remove`) and unqualified
12
+ attachment transfers/removal (`activity.attachFiles`, `activity.downloadFile`,
13
+ `activity.removeFile`) from the supported first-release surface. User reads and
14
+ attachment metadata reads can remain only where already verified.
15
+ - Require explicit consumer warnings in README, API support matrix and changelog,
16
+ plus enforcement preventing excluded calls from being presented as supported.
17
+ Current provisional artifacts expose the existing full API; they are NOT artifacts
18
+ of this hypothetical limited release.
19
+ - Precisely revise the release evidence policy to a reviewed, version-specific
20
+ supported-operation inventory, with unsupported operations recorded as excluded,
21
+ not PASS. Continue requiring clean actual-source contract/read/supported-write
22
+ evidence and complete cleanup. Do not simply set three verification flags true.
23
+ - Rationale: release verified core project/task/activity functionality while deferring
24
+ unqualified User/attachment capabilities. Risk: a narrower SDK/support contract and
25
+ future compatibility changes. This does not resolve or erase issue #14.
26
+ - Approval requires explicit Sheriff policy authorization and independent Deputy
27
+ review of a separate PR. Rebuild/requalify that actual new source and accept new
28
+ hashes. No such policy/source changes are included here.
@@ -0,0 +1,192 @@
1
+ # Sheriff-only manual first-publication preparation
2
+
3
+ **Preparation only. Publication requires Stan's approval of the final source and
4
+ exact artifact hashes.** Live API checks are optional at Stan's discretion. No
5
+ active 5PM trial or subscription is required. The [release policy](../releasing.md)
6
+ supersedes the historical live-test blockers recorded in the frozen manifest.
7
+
8
+ ## Frozen proposed source and artifacts
9
+
10
+ Proposed version: **0.1.0** for both gems. Frozen source baseline:
11
+ `9322e03f75604d7279775dc88c9192add840e76e` (accepted master, not an accepted release).
12
+ Changelog: **0.1.0 — unreleased**. Current artifacts are provisional and blocked.
13
+ A dated changelog/source change requires rebuilding, offline verification and new hashes;
14
+ do not publish these bytes after substituting a different candidate SHA.
15
+
16
+ [Machine-readable provenance/inventories](0.1.0-manual-artifacts.json) records:
17
+
18
+ | Artifact | SHA-256 |
19
+ | --- | --- |
20
+ | `5pm-0.1.0.gem` | `160b09db75dd1cf0aebac21e64f0f6a1216ea437c3155816481a1c7f0faff0e6` |
21
+ | `5pm-mcp-0.1.0.gem` | `8ac7c8a5dc175b7ecd4d934b3c720ee3d4b0f019deacd3c5e94d249a3e22d601` |
22
+
23
+ Build environment: Ruby 4.0.9, RubyGems 4.0.23, Bundler 4.0.23,
24
+ `SOURCE_DATE_EPOCH=1793399403` (source commit timestamp). Two independent builds
25
+ produced identical bytes. Core inventory: 28 files; MCP: eight. MIT licenses, RBS,
26
+ installed load paths and both exact versions passed isolated verification.
27
+
28
+ Both gems require Ruby >=3.2. Supported/tested versions: 3.2, 3.3, 3.4, 4.0.
29
+ [Source CI run 41138526407](https://github.com/scarver2/5pm-ruby/actions/runs/41138526407)
30
+ passed all four jobs (143 examples, RBS/runtime, lint and package verification).
31
+ Core depends on json >=2.6,<4 and net-http >=0.3,<1; MCP depends on 5pm ~>0.1.0
32
+ and mcp ~>1.6.1. The locked dependency set is tested, not every possible version
33
+ within these ranges. MCP is not a core runtime dependency.
34
+
35
+ ## Offline build, inspect and install (no publishing credentials)
36
+
37
+ Use a clean worktree of the exact candidate with locked dependencies installed.
38
+ The prepared local worktree is `/private/tmp/5pm-maiden-voyage-0.1.0/source`.
39
+ The public source can be reproduced as follows; do not overwrite an existing worktree:
40
+
41
+ ```sh
42
+ set -eu
43
+ cd /Users/cora/Projects/OSS/5pm-ruby/sdk
44
+ candidate_sha=9322e03f75604d7279775dc88c9192add840e76e
45
+ source_dir=/private/tmp/5pm-maiden-voyage-0.1.0/source
46
+ # Already executed for the prepared worktree; only create if absent:
47
+ # git worktree add --detach "$source_dir" "$candidate_sha"
48
+ cd "$source_dir"
49
+ test "$(git rev-parse HEAD)" = "$candidate_sha"
50
+ test -z "$(git status --porcelain)"
51
+ export PATH=/Users/cora/.local/share/mise/installs/ruby/4.0.9/bin:$PATH
52
+ export BUNDLE_PATH=/Users/cora/Projects/OSS/5pm-ruby/sdk/vendor/bundle
53
+ export BUNDLE_FROZEN=true
54
+ export SOURCE_DATE_EPOCH=1793399403
55
+ bundle check
56
+ bin/spec
57
+ bin/lint --cache false
58
+ bin/package-mcp
59
+ # bin/package-mcp builds/inspects both gems and verifies disposable installs/RBS.
60
+ ruby bin/verify-package.rb pkg/5pm-0.1.0.gem
61
+ shasum -a 256 pkg/5pm-0.1.0.gem mcp/pkg/5pm-mcp-0.1.0.gem
62
+ ```
63
+
64
+ Explicit standalone isolated-install/load commands, using only bundled dependencies:
65
+
66
+ ```sh
67
+ set -eu
68
+ qualification_home="$(mktemp -d /private/tmp/5pm-install.XXXXXX)"
69
+ trap 'rm -rf -- "$qualification_home"' EXIT
70
+ dependency_path="$(bundle exec ruby -rbundler -e 'puts Bundler.load.specs.map(&:base_dir).uniq.join(File::PATH_SEPARATOR)')"
71
+ for artifact in pkg/5pm-0.1.0.gem mcp/pkg/5pm-mcp-0.1.0.gem; do
72
+ GEM_HOME="$qualification_home" GEM_PATH="$dependency_path" ruby -rrubygems/installer -e \
73
+ 'Gem::Installer.at(ARGV.fetch(0), install_dir: ARGV.fetch(1), ignore_dependencies: true, document: []).install' \
74
+ "$artifact" "$qualification_home"
75
+ done
76
+ GEM_HOME="$qualification_home" GEM_PATH="$dependency_path" ruby -rrubygems -e '
77
+ gem "5pm", "0.1.0"; require "five_pm"
78
+ gem "5pm-mcp", "0.1.0"; require "five_pm/mcp"
79
+ abort "core version mismatch" unless FivePM::VERSION == "0.1.0"
80
+ abort "MCP version mismatch" unless FivePM::MCPAdapter::VERSION == "0.1.0"
81
+ %w[5pm 5pm-mcp].each do |name|
82
+ actual = File.realpath(Gem.loaded_specs.fetch(name).full_gem_path)
83
+ abort "loaded outside disposable GEM_HOME" unless actual.start_with?(File.realpath(ENV.fetch("GEM_HOME")) + File::SEPARATOR)
84
+ end
85
+ puts "Both exact versions loaded from disposable GEM_HOME"
86
+ '
87
+ ```
88
+
89
+ No dependency downloads occur during these installs. The prepared artifact directory
90
+ is `/private/tmp/5pm-maiden-voyage-0.1.0/artifacts`, with the two gems, `SHA256SUMS`
91
+ and `provenance.json`. It is outside Git and outside the frozen source tree.
92
+ Verify it with `cd` to that directory, then `shasum -a 256 -c SHA256SUMS`.
93
+
94
+ ## Preconditions before any push
95
+
96
+ - Preserve historical API observations and failed receipts, including issue #14.
97
+ Stan confirmed the leftover test User was deleted. Another live session or
98
+ qualification run is not a publication prerequisite.
99
+ - Review a dated changelog and final exact source. Pass offline specs, lint, package
100
+ checks, hosted CI and the read-only `bin/release-check VERSION SHA`.
101
+ - Deputy accepts exact artifacts/source; Sheriff separately authorizes the final push.
102
+ - Sheriff verifies his own existing local RubyGems authentication belongs to the
103
+ intended account (immutable ID 239830), permits `push_rubygem`/first publication of
104
+ both names and meets MFA requirements. Cora neither accesses nor uses it.
105
+ - If existing authentication is missing/insufficient, STOP for owner action. Do not
106
+ run signin, generate credentials automatically, add repository secrets, print keys,
107
+ or pass credentials/MFA codes as command-line arguments. No new permanent key is
108
+ part of this procedure. An authenticated browser alone is not CLI authentication.
109
+
110
+ Read-only public gem and owner API requests returned 404 for both names on 2026-10-30.
111
+ No published ownership was observed; this neither reserves names nor proves manual
112
+ push permission. Recheck immediately before publication. Any newly existing gem,
113
+ version conflict, different owner or policy denial means STOP; do not overwrite/yank.
114
+
115
+ Manual `gem push` uses Sheriff's own authenticated account, not OIDC token exchange.
116
+ Thus owner-input action support is a later automation prerequisite, not a reason to
117
+ alter this manual route. Leave the protected OIDC workflow and pending publishers
118
+ unchanged. This distinction does not waive source/evidence/publication approval.
119
+
120
+ ## Sheriff-only stage — DO NOT EXECUTE NOW
121
+
122
+ The following is a reviewed command specification, not a runnable automation added
123
+ to the repository. Execute only after every precondition above and exact artifact
124
+ acceptance. The frozen candidate has an undated changelog and is not a final release candidate.
125
+ If candidate bytes/SHA change, replace the reviewed manifest/hashes only through new
126
+ acceptance; do not edit these values to force an existing approval to match.
127
+
128
+ Run personally in Sheriff's local terminal with shell tracing/recording disabled.
129
+ Use existing local CLI credentials; no API keys, passwords or MFA codes go in chat,
130
+ logs, repository configuration, or agent input. Consult [RubyGems CLI](https://guides.rubygems.org/command-reference/#gem-push),
131
+ [scopes](https://guides.rubygems.org/api-key-scopes/) and
132
+ [MFA](https://guides.rubygems.org/using-mfa-in-command-line/).
133
+
134
+ ```sh
135
+ bash <<'SHERIFF_ONLY'
136
+ set -euo pipefail
137
+ unset GEM_HOST_API_KEY RUBYGEMS_API_KEY BUNDLE_GEM__PUSH_KEY GEM_HOST_OTP_CODE
138
+ # Set these in your terminal from the final reviewed acceptance packet.
139
+ : "${SOURCE_DIR:?Set SOURCE_DIR to your clean local source worktree}"
140
+ : "${ARTIFACT_DIR:?Set ARTIFACT_DIR to the final accepted artifact directory}"
141
+ : "${CANDIDATE_SHA:?Set CANDIDATE_SHA to the final accepted full commit}"
142
+ : "${CORE_SHA256:?Set CORE_SHA256 to the independently accepted core digest}"
143
+ : "${MCP_SHA256:?Set MCP_SHA256 to the independently accepted MCP digest}"
144
+ source_dir="$(cd "$SOURCE_DIR" && pwd -P)"
145
+ artifact_dir="$(cd "$ARTIFACT_DIR" && pwd -P)"
146
+ candidate_sha="$CANDIDATE_SHA"
147
+ [[ "$CORE_SHA256" =~ ^[0-9a-f]{64}$ && "$MCP_SHA256" =~ ^[0-9a-f]{64}$ ]]
148
+ cd "$source_dir"
149
+ test "$(git rev-parse HEAD)" = "$candidate_sha"
150
+ mise exec -- bin/release-check 0.1.0 "$candidate_sha" # Never bypass.
151
+ # Guard against automatic credential issuance when local authentication is absent.
152
+ # This reads only inside Sheriff's local process and prints no key.
153
+ mise exec -- ruby -rrubygems/config_file -e 'abort "STOP: existing local RubyGems authentication required" if Gem.configuration.rubygems_api_key.to_s.empty?'
154
+ cd "$artifact_dir"
155
+ # Digests must come from the independent acceptance packet, not this directory.
156
+ printf '%s %s\n' "$CORE_SHA256" '5pm-0.1.0.gem' \
157
+ "$MCP_SHA256" '5pm-mcp-0.1.0.gem' | shasum -a 256 -c -
158
+ # Core first. These commands are not authorized until final acceptance/push approval.
159
+ mise exec -- gem push --host https://rubygems.org "$artifact_dir/5pm-0.1.0.gem" </dev/tty
160
+ verification_dir="$(mktemp -d)"
161
+ trap 'rm -rf -- "$verification_dir"' EXIT
162
+ cd "$verification_dir"
163
+ mise exec -- gem fetch 5pm --version 0.1.0 --clear-sources --source https://rubygems.org
164
+ cmp "$artifact_dir/5pm-0.1.0.gem" 5pm-0.1.0.gem
165
+ # Only after the exact core artifact is publicly available:
166
+ mise exec -- gem push --host https://rubygems.org "$artifact_dir/5pm-mcp-0.1.0.gem" </dev/tty
167
+ mise exec -- gem fetch 5pm-mcp --version 0.1.0 --clear-sources --source https://rubygems.org
168
+ cmp "$artifact_dir/5pm-mcp-0.1.0.gem" 5pm-mcp-0.1.0.gem
169
+ SHERIFF_ONLY
170
+ ```
171
+
172
+ If propagation delays a fetch, STOP and retry only the read-only fetch/compare once
173
+ availability is confirmed; do not repeat an already successful push. Verify account
174
+ ownership on RubyGems after publication. No automatic retries, yanks, tags or GitHub
175
+ release dispatches are included. No push commands above were executed by Cora.
176
+
177
+ ## Historical proposals and current preparation
178
+
179
+ The [limited-release proposal](limited-first-release-decision.md) and the frozen
180
+ manifest are historical records, not current live-test requirements. Stan has
181
+ removed the live API release gate; no fresh success report is required or fabricated.
182
+ API observations remain documented without changing the supported API implicitly.
183
+
184
+ The historical hashes above remain provisional. After the final source is dated,
185
+ reviewed and offline-tested, rebuild both gems, independently accept their new
186
+ hashes, and provide those values through exported environment variables to the
187
+ owner-only procedure. Keep credentials outside these variables and the repository.
188
+
189
+ —
190
+ Stan Carver II
191
+ Made in Texas 🤠
192
+ https://stancarver.com