Skip to content

Port governance, strategy and release engineering from the working copy - #3

Merged
Shubochandrosarker merged 1 commit into
mainfrom
chore/platform-foundation-sync
Aug 8, 2026
Merged

Port governance, strategy and release engineering from the working copy#3
Shubochandrosarker merged 1 commit into
mainfrom
chore/platform-foundation-sync

Conversation

@Shubochandrosarker

Copy link
Copy Markdown
Contributor

What this changes

Before: this repository carried the product and one CI workflow and nothing else — no contributor guide, no security policy, no release automation, no architecture decisions, no written plan for what comes next. After: all of that is here, ported from the personal working copy (Shubochandrosarker/memberistic), which was built against this same 2.0.0 baseline.

No runtime plugin code moves in this PR. The working copy also contains fixes to class-plugin.php, class-stripe-service.php, class-licensing.php, uninstall.php and readme.txt, plus an integration suite and a plugin directory rename. Each of those is evaluated on its own evidence, with its own tests and its own PR. docs/governance/decisions/0003-controlled-convergence-from-the-working-copy.md records that split and why a wholesale merge was rejected — chiefly that it bundles the directory rename (which makes WordPress treat the upgrade as a different plugin) with a fatal-error fix under one combined justification.

Delivers backlog item P0-3 · Release automation.

Type

  • Documentation
  • Release/CI plumbing

Rules touched

  • None of the above

No runtime code changed. The invariants are unaffected by this PR; docs/strategy/00-master-plan.md now records them in-repo for the first time.

Multi-edit checklist

  • N/A — no new class files, no schema change, no shortcode, no capability, no version bump

Worth noting: this template's first checklist item — "New class files added to the require_once list in Plugin::load_dependencies() — nothing autoloads" — is precisely the trap that P0-11 below fell into. The template arrives already knowing about it.

Documentation

  • An ADR in docs/governance/decisions/ — ADR 0003
  • CHANGELOG.md — deliberately not touched. Nothing here is user-visible; the distributable's contents change only by removing docs/README.md, which was dead links.

Beyond a straight port

Four things were not copied as-is, because copying them as-is would have been wrong:

1. The build logic is shared, not duplicated. The working copy inlines the copy-and-prune build in release.yml. Two inline copies in two workflows drift the first time a dev-only file lands in the repository root — and then CI reports the archive clean while the release ships it. Extracted to bin/build-dist.sh and bin/assert-dist-clean.sh; ci.yml's new dist-guard job runs the same two scripts the release runs, on every PR.

2. The leak-check tests three things independently, because they fail differently:

  • every .distignore entry actually disappeared — catches a pattern that silently stopped matching, which still looks fine in a log that prints what it tried to exclude;
  • an explicit deny-list is absent whatever .distignore says — docs/strategy carries pricing models and revenue targets and must never reach a customer's plugin directory;
  • the files a plugin cannot work without are still present — a pattern greedy enough to ship an empty plugin passes both checks above.

Both failure directions are negative-tested (output below).

3. docs/README.md no longer ships. It is repository navigation: 20 of its 31 links point into docs/strategy, docs/governance, CLAUDE.md and CONTRIBUTING.md, none of which ship. In a customer's wp-content/plugins directory it rendered as a page of dead links. Excluding it fixes that instance; a new markdown link check fails the build on the next one.

4. The unit suite runs on PHP 8.2/8.3/8.4 rather than 8.3 alone, with the Composer cache keyed per version — a shared key lets the first job to finish decide what the other two actually test.

Owner-specific references (issue-template links, CLAUDE.md § This repository, BRANCHING.md § upstream) were reoriented: this repository is now described as primary, the working copy as the drafting tree.

The backlog arrived overstating its own progress

docs/strategy/09-execution-backlog.md came with 21 acceptance criteria ticked, every one describing files that do not exist here — tests/integration/, phpunit-integration.xml, bin/install-wp-tests.sh, the plugin-check job. As source of truth it would have reported P0-4, P0-5 and P0-6 as largely finished when none has started.

All 21 reset to unchecked, plus a provenance banner and a table of what remains available to port. The rule going forward: a box is ticked only when the evidence is in this repository and its CI is green.

⚠️ Found while verifying: the published 2.0.0 fatals on init

Added as P0-11, which outranks everything else on that page.

includes/integrations/class-booking-adapter.php shipped in 2.0.0 but was never added to Plugin::load_dependencies(), and there is no autoloader. It is the only file under includes/ missing from that list (71 listed, 73 on disk; the other is class-plugin.php, required by the bootstrap).

Waiver_Booking_Bridge::register() calls Booking_Adapter::hook() as its first statement, and is registered on init priority 4 whenever the Waiver Manager integration is enabled — whose default is 'yes'. The chain fires on a stock install with no configuration:

init(4) → Waiver_Booking_Bridge::register()
        → Booking_Adapter::hook('waiver_satisfied')
        → Error: Class "…\Integrations\Booking_Adapter" not found

Reproduced by loading only the files load_dependencies() lists and invoking the init path:

Files listed in Plugin::load_dependencies(): 71
Actual .php files under includes/:            73

class_exists('Booking_Adapter') after load_dependencies(): bool(false)
Is the file present on disk?                            : bool(true)
Is it in the require list?                              : bool(false)

--- init(priority 4) -> Waiver_Booking_Bridge::register() ---
Error: Class "WordPressistic\Memberistic\Integrations\Booking_Adapter" not found
>>> FATAL REPRODUCED <<<

Neither php -l nor the unit suite can see this — every file parses perfectly alone, and nothing in the unit suite boots the plugin. Booking_Engine, POS_Bridge and Staff_Dashboard reach the same class.

The fix is not in this PR — it is runtime code and gets its own, with a guard test that fails if any file under includes/ is missing from the require list, so the class of bug cannot recur rather than just this instance.

What I ran

$ find . -name '*.php' -not -path './vendor/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l
  clean   (PHP 8.4.19)

$ find assets -name '*.js' -print0 | xargs -0 -n1 node --check
  clean

$ vendor/bin/phpunit -c phpunit.xml
  ...............................................                   47 / 47 (100%)
  OK (47 tests, 831 assertions)

$ python3 -c "import yaml; [yaml.safe_load(open(f)) for f in ['.github/workflows/ci.yml','.github/workflows/release.yml']]"
  both valid

$ bin/build-dist.sh --zip 2.0.0 && bin/assert-dist-clean.sh --zip 2.0.0
  --- check 1: every .distignore entry is absent from the build ---
  ok: nothing named in .distignore survived the build
  --- check 2: explicit deny-list ---
  --- check 3: product files that MUST ship ---
  --- check 4: no shipped markdown links into excluded material ---
  --- check 5: archive listing ---
  distributable is clean

Guards negative-tested — a guard that never fails is not a guard:

$ # plant docs/strategy in the build
::error::.distignore lists 'docs/strategy' but it is present in the build
::error::forbidden path present in distributable: docs/strategy
distributable is NOT clean

$ # delete a required product directory
::error::required product file missing from distributable: includes
distributable is NOT clean

$ # plant a doc linking into excluded material
::error::docs/LEAKTEST.md links to 'strategy/00-master-plan.md', which is not in the distributable
::error::docs/LEAKTEST.md links to '../CONTRIBUTING.md', which is not in the distributable
distributable is NOT clean

$ # restore docs/README.md into the build (the original defect)
  errors raised: 25

Distributable contents after the change — 2.1 MB tree, 516,647-byte zip:

CHANGELOG.md  LICENSE  README.md  SECURITY.md  THIRD-PARTY-LICENSES.md
assets/  docs/  includes/  index.php  languages/
memberistic-membership-solutions.php  readme.txt  templates/  uninstall.php

docs/: HOOKS.md INSTALL.md INTEGRATIONS.md UPGRADE-2.0.md entitlements.md guest-pass-audit.md

What I did NOT test

  • release.yml end to end. It is tag-triggered and no tag was pushed. Its build and leak-check steps are the same two scripts exercised above, but the version-consistency check, the artifact upload and the draft-release step have not run. First v2.0.1 tag is the real test.
  • shellcheck on the two new scripts. Not installed in this environment. bash -n parses both. Reported as not-run, not as a pass.
  • Plugin Check. Not ported in this PR (P0-4).
  • Any WordPress runtime behaviour. No integration harness exists here yet (P0-0), and no runtime code changed, so there was nothing to exercise.
  • The distributable installed on a clean WordPress. It builds and its contents are asserted; it has not been unzipped into a real install. That is a P0-2 release-gate step.

Risk and rollback

  • Blast radius: documentation, CI and packaging only. No PHP, JS, CSS or template file changed, so no installed site can behave differently. The one behavioural change is to a future artifact: the built zip no longer contains docs/README.md.
  • Rollback: revert the merge commit. release.yml is tag-triggered and inert until a v* tag is pushed; dist-guard is a new job, so reverting simply removes it.
  • Data migration to reverse? No.

Generated by Claude Code

`Wordpressistic/memberistic` carried the product and one CI workflow and
nothing else — no contributor guide, no security policy, no release
automation, no architecture decisions, no written plan. The personal working
copy has all of it, built against this same 2.0.0 baseline.

Ported: CLAUDE.md, CONTRIBUTING.md, SECURITY.md, SUPPORT.md,
CODE_OF_CONDUCT.md, .editorconfig, CODEOWNERS, issue and PR templates,
docs/governance/ and docs/strategy/, and release.yml. No runtime plugin code
moves in this commit — the working copy's fixes to class-plugin.php,
class-stripe-service.php, class-licensing.php and uninstall.php are each
evaluated on their own evidence, with their own tests and their own PR.
ADR 0003 records that split and why a wholesale merge was rejected.

Release engineering, beyond a straight port:

- The copy-and-prune build and the leak-check move to bin/build-dist.sh and
  bin/assert-dist-clean.sh, so ci.yml's new dist-guard job runs the *same*
  code the release workflow runs. Inline copies in two workflows drift the
  first time a dev-only file lands in the root, and then CI reports the
  archive clean while the release ships it.
- The leak-check tests three things independently: that every .distignore
  entry actually disappeared, that an explicit deny-list is absent whatever
  .distignore says, and that the files a plugin cannot work without are still
  present — a pattern greedy enough to ship an empty plugin passes the first
  two checks. Both failure directions are negative-tested.
- docs/README.md no longer ships. It is repository navigation: 20 of its 31
  links point into docs/strategy, docs/governance, CLAUDE.md and
  CONTRIBUTING.md, so in a customer's plugin directory it rendered as a page
  of dead links. A markdown link check now fails the build on the next one.
- The unit suite runs on PHP 8.2/8.3/8.4 rather than 8.3 alone, with the
  Composer cache keyed per version so one job's vendor/ cannot decide what
  the other two test.

The backlog arrived with 21 acceptance criteria ticked, every one describing
files absent from this repository — it would have reported P0-4, P0-5 and
P0-6 as largely done here when none has started. All reset to unchecked, with
a provenance banner and an inventory of what remains available to port.

Added P0-11, which outranks the rest of the page: class-booking-adapter.php
ships in 2.0.0 but is missing from Plugin::load_dependencies() and there is no
autoloader, so Waiver_Booking_Bridge::register() fatals on init priority 4 on
every stock install. Reproduced by loading only what load_dependencies() lists
and invoking the init path. Fix follows in its own PR.

Verified: php -l clean on 8.4; PHPUnit 47 tests / 831 assertions / 0 failures;
node --check clean; both workflows parse as YAML; distributable builds clean
and both guards fail correctly when a leak or a missing product file is
planted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgGmwdEzTdVas4VTDzJQ5m
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants