# Development Workflow ## Scope and Authority Status: implementation and verification guidance, 2026-09-11. D01-D10 are settled; this document specifies build checks without claiming they exist or passed. This document owns change workflow, engineering practice and verification records. Follow [documentation governance](../governance/documentation.md) for authority, approval and documentation updates, and the [source map](../governance/source-map.md) for lineage. Use [implementation architecture](architecture.md) for runtime boundaries and [security and operations](security-and-operations.md) for operational controls. Existing marketing code is a reuse inventory, not a mandatory starting domain model or an API compatibility contract. ## Workspace and GitHub Authority The local workspace/filesystem is the implementation workspace for the current development session, not an authoritative production release source. Inspect the actual files directly and use the pinned local tools to establish runtime evidence. Production-eligible release source is the exact commit checked out by the GitHub workflow in [StuMayne74/Idiosyncrasy](https://github.com/StuMayne74/Idiosyncrasy), currently represented locally by the `origin` remote and the `main` branch. GitHub is the source of truth for production release source and version-control record, not a substitute for local execution evidence. Use `git status`, `git diff`, `git log`, branch information and remote comparisons to understand change history, review scope and synchronization. Use tests, diagnostics, filesystem inspection and directly observable runtime checks to establish implementation behavior. Do not treat a clean worktree, pushed commit or GitHub file view as proof that code works, and do not treat ignored local runtime state as repository content. A deployable package must carry the exact GitHub repository, full commit SHA, derived release ID, artifact digest and non-secret build identity. Keep machine-specific secrets, database state, runtime-generated files, deployment metadata and release staging output outside the tracked source set. The repository's ignore rules are part of this boundary; review them when adding new local-development artifacts. Report both implementation evidence and relevant Git synchronization state when a task depends on them, without printing credentials. The non-deploying [target release workflow](../../.github/workflows/build-target-release.yml) checks out `${{ github.sha }}` and publishes a package artifact. `bin/target-release.ps1` is a consumer/staging operator for that package and fails when provenance is absent; it does not package the arbitrary local working tree. The former direct-upload path has been removed. The GitHub workflow builds, verifies and deploys the exact public application package only. It does not receive staging database credentials, publish a staging provisioning artifact, reset a staging database or run migrations, Genesis or platform opening. Those STAGING database operations are performed only through the protected internal reset/provisioning scripts, with `php bin/reset-staging-database.php --database=staging` followed by `php bin/provision-target.php --database=staging` when explicitly authorized. Production provisioning is deferred and has no active CLI route in the current development phase. The selected-database preflight binds its `information_schema` emptiness query to the database name resolved by `TargetRuntimeConfig`, after the selector identity has been validated. It does not use the production identity as a schema-query fallback, and missing or unsafe configured identity fails closed. The separate `bin/reset-staging-database.php --database=staging` command is a CLI-only, staging-only reset contract. It requires `IDY_STAGING_RESET_CONTEXT=staging`, the literal `IDY_STAGING_RESET_CONFIRMATION=RESET_STAGING_DATABASE`, matching `IDY_TARGET_DB_NAME` and `IDY_STAGING_DB_NAME`, a non-production staging identity, and a dedicated `IDY_STAGING_RESET_DB_*` administrative capability. The capability must target the configured database host and port and use a distinct database user; it drops and recreates only the resolved staging database, then verifies the recreated identity and emptiness through the existing preflight. The command remains in the private internal provisioning bundle and is not invoked by GitHub Actions. The separate [bin/purge-non-spearmint-businesses.php](../../bin/purge-non-spearmint-businesses.php) command is an unexecuted, staging-only cleanup implementation. It requires `IDY_STAGING_PURGE_CONTEXT=staging`, `IDY_STAGING_PURGE_CONFIRMATION=PURGE_NON_SPEARMINT_BUSINESSES`, a validated staging database identity and an explicit `--execute` argument before mutation. It uses the established `IDY_TARGET_DB_NAME` staging connection from `.env.local` when `IDY_STAGING_DB_NAME` is absent; the configured staging identity is currently `idio2plat_staging`. Ordinary invocation is a read-only inventory. It must not be used against production or legacy `organizations`; its hard-delete behavior is a specifically requested staging exception and remains subject to dependency-inventory review and recovery approval. ## Start a Scoped Change 1. Identify the user-visible outcome and its canonical owner; distinguish a capability composition from new reusable execution behavior. 2. Read the nearby implementation, its current evidence, the settled semantic contract and chosen mechanism before selecting an approach. 3. State a falsifiable local hypothesis, a small implementation change and a focused check that could disprove it. 4. Confirm permission, identity, history and no-AI behavior for the affected boundary. 5. Implement only the approved slice; preserve unrelated work and avoid incidental rewrites. 6. Run the cheapest meaningful check immediately, then broaden only for the change's actual risks. 7. Record evidence, remaining gaps and documentation changes; do not convert a successful syntax check into a claim of functional verification. An unsupported capability or unavailable test prerequisite is an implementation limitation, not an open D01-D10 decision. Specify the smallest mechanism that enforces the contract; do not remove semantic distinctions to simplify a demo. Do not fill a design gap by copying old client/admin assumptions, domain tables or fixed workflow lists. No migration, old-data import, compatibility endpoint or legacy feature parity is required unless separately requested. ## Engineering Boundaries Controllers handle HTTP parsing, transport validation, authenticated context and responses. Services contain business rules and orchestration; services may call other services through explicit dependencies. Repositories contain database access and data shaping, returning structured data without deciding business policy. Pass required scope explicitly into repository operations; never trust a request's object identifier without authorization. Use prepared statements for SQL values and explicit allowlists for structural query choices such as sorting. Keep queries efficient and bounded; choose indexes from measured access patterns under the approved persistence model. Templates render authorized data with context-appropriate escaping; approved rich-content sanitization is a separate operation. Provider formatting and protocol details belong in adapters, not in core services or view templates. For continued PHP work, retain `XController`, `XService` and `XRepository` naming in those layers. Use `XJob` only when an approved job runtime exists; naming a class does not schedule or execute it. Prefer constructor injection, small focused methods and explicit dependencies over globals or service locators. Do not introduce direct SQL into new or materially changed controllers or services. Scope any necessary legacy refactor to the behavior being changed; do not perform an unsolicited repository-wide rewrite. The former `app/Services/CampaignService.php` is a limited example of injected persistence and date/input validation. The former `app/Repositories/CampaignRepository.php` provides query implementation evidence, not a target campaign schema. Neither example establishes the new type system, permission policy, workflow runtime or domain vocabulary. ## HTTP and UI Changes Use AJAX by default for non-navigation browser actions, retaining safe conventional submission fallback where practical. Require server-side CSRF verification for every state-changing browser request, including multipart uploads and asynchronous calls. Return structured JSON for asynchronous callers; validate response status and shape before updating the UI. Retain user input after validation or network failure; re-enable controls and prevent duplicate in-flight submissions. Show a meaningful pending state and distinct success, validation, authorization, unavailable and partial-failure results. Never use a hidden or disabled control as the only access restriction. Use accessible labels, keyboard-operable actions and focus management from the [UI system](ui-system.md). Describe the effect of context changes and stale data; do not assume the old selected-client switch is the future tenant selector. For uploads, check size and server-detected type, sanitize approved SVG, generate storage names and clean up unsuccessful replacements. For editable/generated markup, test unsafe protocols, event attributes, script injection and external resource behavior at rendering boundaries. Approval or publication controls must reflect backend authority and actual action results, not optimistic visual success alone. ## Extending Behavior Follow [composition and extensibility](../product/definition.md#composition-and-extensibility) before adding an engine or package. New types and configuration must use the existing conceptual foundation; detailed rules belong to [objects and types](../architecture/objects-and-types.md). Business Graph and Business DNA remain one model under [business model](../architecture/business-model.md). New business semantics route to [semantics](../architecture/semantics.md), not undocumented helper conventions. Adapters follow [communication and integrations](../architecture/communication-and-integrations.md); no provider-specific core or mandatory universal publishing interface is implied. No general worker, module loader or module registration command is currently configured. For async work, implement the specified durable execution/claim/recovery mechanism in [implementation architecture](architecture.md#async-work-and-extensions), retaining identity, retries, cancellation, idempotency, telemetry and deployment evidence. Before introducing packaging, explain registration, dependencies and lifecycle; creating a module directory alone is not an executable installation procedure. Keep workflow meaning with [workflows and automation](../architecture/workflows-and-automation.md) and events with [time and events](../architecture/time-and-events.md). ## Available Checks and Prerequisites The canonical automated test runner is `.\bin\run-tests.ps1`, using the pinned PHP 8.3.33 executable and PHPUnit 11.5.56 against the isolated MariaDB test environment. The runner provisions or reuses `IDY_TEST_IDY002`, sets the `IDY_TEST_DB_*` variables from the local connection configuration without printing credentials, captures PHPUnit stdout/stderr separately, relays both streams and returns PHPUnit's exit code. The repaired runner invokes the PHAR with PowerShell's call operator so workspace paths containing spaces remain one argument; it does not force-load extensions already enabled by the pinned runtime. The canonical focused invocation is: ```text .\bin\run-tests.ps1 tests/Unit/SomeTest.php ``` Every invocation of `.\bin\run-tests.ps1` first refreshes the isolated PHPUnit database from an authorized staging connection. The refresh reads missing staging values from the ignored local `.env.local` file without overriding existing process settings, exports staging through the pinned MariaDB client, recreates only the fixed local `IDY_TEST_IDY002` database, imports the snapshot there, and removes temporary dump and credential files. It refuses loopback staging sources and never changes staging. Validate configuration without connecting or refreshing with `.\bin\refresh-test-database-from-staging.ps1 -ValidateOnly`; the refresh script remains available for explicit diagnostics. Use a test file or PHPUnit filter as the argument. A result is runtime-verified only when PHPUnit reports its test/assertion counts and the runner returns exit code `0`; static diagnostics or visible command launch are not test evidence. If the runner produces no usable output, retry the same canonical invocation once. If the second attempt is empty or unusable, record an execution-channel limitation separately from application failure and do not search for alternate runtimes, shells, PHPUnit copies or test mechanisms. If PHPUnit produces a real nonzero result, preserve the reported failures and repair the application/test boundary in a later scoped task. Integration fixtures use the test-only [TestDatabaseContext](../../tests/Support/TestDatabaseContext.php) for PDO validation, `IDY_TEST_` database scope and deterministic foundation-table reset. This helper does not create User (legacy Type name Principal), Business or authority fixtures; those remain explicit in each test because hiding governance preconditions would make authorization failures harder to diagnose. There is no general worker command or package-install workflow for the new platform. Do not invent `npm test`, Composer scripts, PHPUnit configuration, queue commands or container setup. When adding a real suite, document its dependencies, isolated fixtures and exact command after it works. For changed PHP source, the available syntax-check convention is: ```text php -l path/to/changed-file.php ``` This requires a suitable PHP CLI on the operator's PATH and only checks syntax, not database behavior, security or integration fidelity. Check runtime extensions against the chosen behavior: PDO with `pdo_mysql` for old persistence, OpenSSL for current crypto, cURL for WordPress, DOM/libxml for sanitization, and Fileinfo for MIME detection. Do not claim these extensions or a local database are installed merely because source calls them. The old setup command and its restrictions are documented in [old-schema setup](security-and-operations.md#old-schema-setup); do not run it as a test or new-platform bootstrap. Never point destructive fixtures, setup or experimental requests at shared/production data or live external publishing targets. ## Architecture Conformance Gates These are required checks to implement as the relevant feature is built, not tests claimed to exist today. | Gate | Evidence that must be recorded | | --- | --- | | Universal representation | Valid/invalid objects across business data, types, relationships, configuration, knowledge, workflows/nodes, events, views, permissions and integration tools; retain valid scalar nesting | | Object/definition distinction | Create or inspect an empty object, a definition-less object and a descriptive definition; prove that intended configuration does not silently mutate actual graph reality or migrate instances | | Derived State propagation | Change a value, constituent or relationship and verify the resulting derived condition/transition for containing objects, including materialized-state invalidation or recomputation | | Type and relationship integrity | Missing types/endpoints, invalid values and unauthorized changes fail under the approved bootstrap/evolution/cardinality decisions | | Relationship endpoint retention | Remove or retire endpoint data and verify that required relationship identity/history remains addressable and enters explicit resolution rather than silent cascade destruction | | Cross-capability reuse | The same addressed data participates in more than one business capability without a parallel domain store | | Human operation and no AI | Core create/edit/read/workflow behavior still works with no provider configured and with provider timeout/failure | | Structured retrieval | Type/relationship/state/time/permission filters constrain results; optional semantic retrieval cannot bypass authorization | | Knowledge | Unstructured material retains source, authority and historical identity; inference is not silently approved policy | | Isolation | Cross-tenant object reads, writes, relationships, files, search results and integration operations fail without leaking data | | History and explanation | A user can trace why an action occurred to actor, applicable definitions, evidence and outcome under agreed temporal rules | | Execution | Invalid transitions, duplicates, retries, waits, concurrent changes and cancellation follow approved workflow semantics | | Dependency resolution and bounded cycles | Distinguish resolved, legitimately absent, unresolved, incompatible and execution-failure conditions; reject fabricated inputs and pause/record detectable or runtime-discovered cycles within a finite bound | | Authority is not authorship | Object creation, relationship connectivity, definition authorship, assertion and AI output never create permission or adoption without explicit governance | | Generated presentation | Authorized forms/views render from definitions, validate edits and meet interaction/accessibility acceptance | | Controlled AI output | Provider output is schema-validated, authorized and approved where required before deterministic execution | | Evolution | Observation, suggestion, review, implementation and measurement stay distinguishable and traceable | Canonical details for retrieval, knowledge, views, AI and evolution remain in [retrieval](../architecture/retrieval.md), [knowledge](../architecture/knowledge.md), [views and content](../architecture/views-and-content.md), [AI](../architecture/ai.md), and [analytics and evolution](../architecture/analytics-and-evolution.md). Use [identity and governance](../architecture/identity-and-governance.md) for access and approval semantics rather than inventing local policy precedence. ## Invariant Test Specification **Chosen mechanism:** PHPUnit 11 for PHP 8.3.33, distributed as a pinned verified PHAR without Composer, unit fixtures for pure type/Policy evaluation and isolated MariaDB 10.5.26/InnoDB fixtures for persistence tests. PHPUnit 11 requires PHP 8.2 or newer, so the selected major version is compatible with the production CLI; the exact patch release must be pinned and smoke-tested against PHP 8.3.33. The test bootstrap loads the first-party runtime autoloader; document the locked PHAR version, SHA-256 and exact invocation when the suite is installed. Do not use SQLite or in-memory doubles as proof of InnoDB concurrency/commit behavior. Two independent connections exercise races. Fault injection belongs at repository/transaction/adapter boundaries, not in production bypass endpoints. **Current status:** these remain named test specifications beyond the bounded slices evidenced in [status](status.md). Record any new reproducible command, environment and result there; the canonical runner is `.\bin\run-tests.ps1`. | Required test case | Observable invariant | |---|---| | Unauthorised mutation | Invalid user, absent/revoked membership or missing scoped grant leaves authoritative state/version unchanged; denial does not disclose inaccessible objects. | | Graph is not authority | Adding a business relationship to a protected object never creates a mutation/read grant. | | Stale concurrent updates | Two connections submit the same expected version; one may commit and the other is rejected stale, with no overwritten state or orphan success history. | | Coherent state/history | Inject failure writing a required revision, transition or provenance record; the business state/version and success evidence roll back together. | | Failed Action retained | A durably admitted Action Execution and Attempt remain addressable after rejection/failure, without a successful business effect. | | Duplicate Command | Concurrent/repeated identical Command delivery produces one semantic effect and result; identity reused with a different fingerprint is rejected. Real retries preserve previous Attempts. | | Unknown external outcome | Fake adapter accepts then times out; result remains Unknown/Reconciliation and no unsafe resend occurs. New evidence produces an attributable reconciliation result. | | Lost commit acknowledgement | Inject connection loss at commit acknowledgement; authoritative lookup can establish committed result, otherwise status stays unknown rather than guessed failed. | | AI cannot self-authorise | Forged permission, approval or instructions in AI output/retrieved content fail the same boundary as human/API input. | | Shared mutation path | UI/API/Workflow/AI adapter tests all reach the same Command service; no adapter or ordinary repository caller has a bypass write route. | | Historical retirement | Active queries omit retired objects; authorised historical resolution remains possible where retention permits. | | Addressable transitions | Each lifecycle change has an independent address, before/after/version references and links to Command, actor and authority. | | Durable provenance | Source, acting user, Business, technical executor, Command, Attempt and relevant definition/Policy versions survive commit and process restart. | | Non-destructive assertions | Both original and contradictory accounts retain source/time/identity; standing changes and human resolution are separate addressable evidence. | | Policy conflict and exception | Undefined deterministic resolution blocks effects pending authorised Decision; exception/refinement relationship alone never grants precedence or bypass. | | Revocation before commit | A changed grant, delegation or Policy dependency invalidates obsolete permission/approval reuse; previous committed Actions remain explainable. | | Materialised determination | Scope cannot broaden on materialisation; dependency change prompts explicit re-evaluation; invalidating a determination does not invalidate its underlying Validation or erase past reliance. | | Recovery and resumption | Restart worker with persisted execution/attempt; reconcile before repeat, keep definition binding and recheck current dependencies. | | Controlled genesis | Repeated manifest does not duplicate identity/types/Business; mismatched stores fail; ordinary web callers cannot invoke bootstrap authority. | | First milestone | Login, select authorised Business, inspect objects and perform a declared transition; state, transition, Command and provenance are inspectable without AI. | | User lifecycle | Registration creates pending state only; confirmation activates exactly once; expired, mismatched, replayed and concurrent-losing challenges cannot authenticate or issue authority; duplicate identities do not enumerate accounts. | | Confirmation delivery | Registration does not expose a raw challenge; the confirmation message is purpose-bound; accepted/failed/unknown delivery remains distinct; resend rate limits and supersession prevent stale challenge use. | | Profile authorization | An authenticated User can mutate only its permitted profile fields through an exact Operation and the shared Command boundary; forged User, identity, Business, authority or media-owner fields are rejected. | | Profile image | Validated image bytes are protected, referenced by an addressable media object, safely rendered, authorized on read, and correctly replaced/removed without orphaned or unrelated-file effects. | | Business lifecycle and entitlement | Confirmed Users see only authorized Businesses; no-Business state is explicit; `business.create` is free and never entitlement-gated and creates the server-resolved Business/Membership/governance atomically; inactive entitlement (intended future architecture; all Businesses are currently treated as active and no entitlement is enforced) blocks Business-scoped operations without deleting authority or identity. | | Authenticated shell | Desktop/mobile rendering keeps one accessible top-right User control with image fallback, profile destination and logout; unauthenticated views do not expose authenticated controls; logout destroys/revokes the session as specified. | Write these tests with each vertical slice, then run the integrated suite for the milestone. Keep fake external effects isolated; do not run setup against the old application database or use live provider credentials. A documented test name is not evidence that the architecture is enforced. ## Documentation and Release Gates For documentation-only changes, validate headings, relative links and anchors, source coverage, status wording and absence of secrets. Report canonical targets not yet present during coordinated authoring as unresolved links; recheck them before the canonical switch. Do not link retired source documents as active instructions; record their lineage in the source map instead. Keep observed, proposed, accepted and behavior-verified statements distinguishable; approval does not mean implementation exists. Update [status](status.md) with the precise requirement, code path, check, date/environment and limitations of new evidence. Preserve useful legacy evidence separately with an explicit disposition; do not label all old functionality verified or secure. A behavior release needs focused automated or reproducible manual results, negative/security checks, operational prerequisites and recovery ownership. For UI changes, include pending/error/empty states, keyboard use, mobile layout and actual control behavior. For integrations, include unavailable capabilities, credential redaction, remote failure and sandbox/draft-only effects. Document any test that could not run and why; passing lint is not a substitute for these gates. ## Implementation Handoff Follow [implementation architecture](architecture.md) and the [milestone priority](../product/roadmap.md#implementation-priority). The semantic contract is SETTLED and the mechanism is SPECIFIED; any slice without implementation evidence remains NOT BUILT. Bounded implemented slices and their evidence are recorded in [status](status.md). Do not create new decision IDs, reopen D01-D10 or confuse a missing fixture/DDL detail with missing architecture approval.