toga-ai 1.0.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.
Files changed (62) hide show
  1. package/.claude/settings.json +119 -0
  2. package/.claude-plugin/marketplace.json +87 -0
  3. package/.claude-plugin/plugin.json +22 -0
  4. package/CLAUDE.md +161 -0
  5. package/README.md +72 -0
  6. package/agents/framework-pattern-checker.md +67 -0
  7. package/agents/harness-optimizer.md +102 -0
  8. package/agents/knowledge-writer.md +62 -0
  9. package/agents/php-build-resolver.md +51 -0
  10. package/agents/php-reviewer.md +51 -0
  11. package/agents/planner.md +88 -0
  12. package/agents/session-capture.md +101 -0
  13. package/agents/sql-reviewer.md +67 -0
  14. package/contexts/dev.md +43 -0
  15. package/contexts/research.md +49 -0
  16. package/contexts/review.md +37 -0
  17. package/knowledge/1.0/apps/library/INDEX.md +5 -0
  18. package/knowledge/1.0/apps/library/architecture.md +105 -0
  19. package/knowledge/1.0/apps/worker/INDEX.md +5 -0
  20. package/knowledge/1.0/apps/worker/architecture.md +223 -0
  21. package/knowledge/1.0/standards/backend-php.md +450 -0
  22. package/knowledge/2.0/apps/_underscore/INDEX.md +6 -0
  23. package/knowledge/2.0/apps/_underscore/architecture.md +183 -0
  24. package/knowledge/2.0/apps/_underscore/features/recursive-item-fulfillments.md +111 -0
  25. package/knowledge/2.0/apps/api2/INDEX.md +5 -0
  26. package/knowledge/2.0/apps/api2/architecture.md +162 -0
  27. package/knowledge/2.0/apps/worker2/INDEX.md +6 -0
  28. package/knowledge/2.0/apps/worker2/architecture.md +127 -0
  29. package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +135 -0
  30. package/knowledge/2.0/standards/backend-php.md +710 -0
  31. package/knowledge/CONVENTIONS.md +117 -0
  32. package/knowledge/INDEX.md +19 -0
  33. package/knowledge/clients/.gitkeep +0 -0
  34. package/knowledge/registry.json +7 -0
  35. package/knowledge.js +384 -0
  36. package/mcp-configs/README.md +72 -0
  37. package/mcp-configs/mcp-servers.json +23 -0
  38. package/package.json +50 -0
  39. package/rules/README.md +53 -0
  40. package/rules/common/coding-style.md +123 -0
  41. package/rules/common/git-workflow.md +72 -0
  42. package/rules/common/security.md +118 -0
  43. package/rules/common/testing.md +74 -0
  44. package/rules/php/app-framework.md +104 -0
  45. package/rules/php/underscore-framework.md +111 -0
  46. package/scripts/harness.js +605 -0
  47. package/scripts/hooks/evaluate-session.js +55 -0
  48. package/scripts/hooks/post-edit-validate.js +102 -0
  49. package/scripts/hooks/session-end.js +13 -0
  50. package/scripts/hooks/session-start.js +57 -0
  51. package/scripts/install.js +611 -0
  52. package/scripts/pre-commit +46 -0
  53. package/skills/capture/SKILL.md +294 -0
  54. package/skills/code-review/SKILL.md +140 -0
  55. package/skills/create-elastic-beanstalk/SKILL.md +217 -0
  56. package/skills/harness-audit/SKILL.md +152 -0
  57. package/skills/kickoff/SKILL.md +151 -0
  58. package/skills/php-patterns/SKILL.md +296 -0
  59. package/skills/session-resume/SKILL.md +156 -0
  60. package/skills/session-save/SKILL.md +158 -0
  61. package/skills/sync-team-skills/SKILL.md +87 -0
  62. package/sync-skills.js +71 -0
@@ -0,0 +1,53 @@
1
+ # Rules Directory
2
+
3
+ Rules are **always-follow guidelines** that are loaded automatically into every Claude Code session. Unlike skills (which are on-demand slash commands), rules are passive context — Claude reads them and applies them without being asked.
4
+
5
+ ## What belongs here
6
+
7
+ Rules files contain direct imperatives: coding style, security requirements, git workflow, framework conventions. They do not contain architecture documentation (that goes in `knowledge/`) or how-to instructions (that goes in `skills/`).
8
+
9
+ ## Directory structure
10
+
11
+ ```
12
+ rules/
13
+ ├── README.md ← this file
14
+ ├── common/ ← apply to all repos and both frameworks
15
+ │ ├── coding-style.md ← language-agnostic style rules
16
+ │ ├── git-workflow.md ← branching, commit messages, PR rules
17
+ │ ├── security.md ← security rules (critical — always followed)
18
+ │ └── testing.md ← test requirements
19
+ └── php/ ← PHP-specific rules
20
+ ├── app-framework.md ← 1.0 App_ framework rules
21
+ └── underscore-framework.md ← 2.0 _underscore framework rules
22
+ ```
23
+
24
+ ## How to install
25
+
26
+ Run from the team knowledge repo root:
27
+
28
+ ```sh
29
+ node scripts/install.js
30
+ ```
31
+
32
+ This copies all files from `rules/` into `.claude/rules/toga/` in the target project. Claude Code automatically loads rules from `.claude/rules/`.
33
+
34
+ If you want to install into a specific project:
35
+
36
+ ```sh
37
+ node scripts/install.js /path/to/project
38
+ ```
39
+
40
+ ## How to add new rules
41
+
42
+ 1. Create a new `.md` file in the appropriate subdirectory (`common/` or `php/`).
43
+ 2. Write direct imperatives — "Never do X", "Always do Y". No meta-commentary.
44
+ 3. Keep each file under 200 lines.
45
+ 4. Run `node scripts/install.js` to push the new rule to all installed projects.
46
+ 5. Commit the new rule file to this repo so it's available to the whole team.
47
+
48
+ ## Why rules instead of CLAUDE.md
49
+
50
+ `CLAUDE.md` is project-specific context. Rules in `.claude/rules/` are team-wide always-on standards. Keeping them separate means:
51
+ - Updating a rule updates it for the whole team on next install.
52
+ - Project `CLAUDE.md` stays focused on project-specific context.
53
+ - Rules can be audited separately from project docs.
@@ -0,0 +1,123 @@
1
+ # Coding Style Rules
2
+
3
+ These rules apply to all code in all TOGA Technology repos, regardless of language or framework.
4
+
5
+ ## Error handling
6
+
7
+ Never use bare `catch` blocks. Never catch and silently ignore an exception.
8
+
9
+ ```php
10
+ // WRONG
11
+ try {
12
+ $result = $db->query($sql);
13
+ } catch (Exception $e) {
14
+ // nothing here
15
+ }
16
+
17
+ // WRONG
18
+ } catch (Exception $e) {}
19
+
20
+ // CORRECT
21
+ } catch (PDOException $e) {
22
+ error_log('DB query failed: ' . $e->getMessage());
23
+ throw $e; // or handle specifically
24
+ }
25
+ ```
26
+
27
+ Always catch the most specific exception type available. Catching `Exception` as a last resort is acceptable only if you log it and re-throw or return an error response.
28
+
29
+ Never use `@` error suppression. Fix the underlying issue instead.
30
+
31
+ ## Dangerous functions
32
+
33
+ Never use `eval()` on user input or on any string that contains user-controlled data.
34
+
35
+ Never use `exec()`, `shell_exec()`, `system()`, `passthru()`, or `popen()` with user input. If shell execution is required, use an allowlist of commands and escape arguments with `escapeshellarg()`.
36
+
37
+ ## Function parameters
38
+
39
+ Functions with 4 or more parameters must use an associative array argument or a named-parameters pattern. Do not define functions with 4+ positional parameters.
40
+
41
+ ```php
42
+ // WRONG
43
+ function createOrder($userId, $items, $addressId, $couponCode, $notes) { ... }
44
+
45
+ // CORRECT
46
+ function createOrder(array $params): Order {
47
+ // $params['user_id'], $params['items'], $params['address_id'], etc.
48
+ }
49
+ ```
50
+
51
+ ## Return early
52
+
53
+ Use guard clauses and return early. Do not nest the happy path inside conditionals.
54
+
55
+ ```php
56
+ // WRONG
57
+ function getUser(int $id): ?User {
58
+ if ($id > 0) {
59
+ $user = $this->db->find($id);
60
+ if ($user) {
61
+ return $user;
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+
67
+ // CORRECT
68
+ function getUser(int $id): ?User {
69
+ if ($id <= 0) return null;
70
+ return $this->db->find($id);
71
+ }
72
+ ```
73
+
74
+ ## Dead code
75
+
76
+ Never commit commented-out code. Delete it. If you need to preserve a previous approach, that is what git history is for.
77
+
78
+ ```php
79
+ // WRONG
80
+ // $result = $this->oldMethod($id);
81
+ $result = $this->newMethod($id);
82
+ ```
83
+
84
+ ## Magic numbers
85
+
86
+ Never use unexplained numeric or string literals. Define named constants.
87
+
88
+ ```php
89
+ // WRONG
90
+ if ($attempts > 3) { ... }
91
+ sleep(86400);
92
+
93
+ // CORRECT
94
+ const MAX_LOGIN_ATTEMPTS = 3;
95
+ const SECONDS_PER_DAY = 86400;
96
+
97
+ if ($attempts > self::MAX_LOGIN_ATTEMPTS) { ... }
98
+ sleep(self::SECONDS_PER_DAY);
99
+ ```
100
+
101
+ ## Single responsibility
102
+
103
+ Each function does one thing. If a function's name requires "and" to describe it, split it.
104
+
105
+ Functions longer than 40 lines are a signal to review for decomposition — not a hard rule, but a strong prompt to consider whether the function has grown beyond one responsibility.
106
+
107
+ ## Type declarations
108
+
109
+ All public and protected methods must have return type declarations. Parameter types must be declared for all non-variadic parameters. Use PHP 7.4+ typed properties for class attributes.
110
+
111
+ ```php
112
+ // WRONG
113
+ public function process($data) {
114
+ ...
115
+ }
116
+
117
+ // CORRECT
118
+ public function process(array $data): bool {
119
+ ...
120
+ }
121
+ ```
122
+
123
+ Use `?Type` for nullable. Use `void` for no return value. Do not omit the return type.
@@ -0,0 +1,72 @@
1
+ # Git Workflow Rules
2
+
3
+ ## Branch protection
4
+
5
+ Never commit directly to `_main`. All changes go through feature branches and pull requests.
6
+
7
+ Never force-push to `_main` under any circumstances.
8
+
9
+ ## Branch naming
10
+
11
+ Use one of these prefixes followed by a short hyphenated description:
12
+
13
+ - `feature/short-description` — new functionality
14
+ - `fix/short-description` — bug fix
15
+ - `hotfix/short-description` — urgent production fix
16
+ - `refactor/short-description` — code restructuring with no behavior change
17
+ - `docs/short-description` — documentation only
18
+
19
+ Examples: `feature/batch-order-processing`, `fix/null-customer-email`, `hotfix/sqs-timeout`
20
+
21
+ Keep the description short (3–5 words). Use hyphens, not underscores.
22
+
23
+ ## Commit messages
24
+
25
+ Format: `type: short description`
26
+
27
+ Types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`
28
+
29
+ - `feat: add batch order processing worker`
30
+ - `fix: null check on customer email before dispatch`
31
+ - `refactor: extract order validation to dedicated class`
32
+ - `docs: document recursive item fulfillment feature`
33
+ - `test: add regression test for empty batch edge case`
34
+ - `chore: update composer dependencies`
35
+
36
+ The description is lowercase, present tense, under 72 characters. No period at the end.
37
+
38
+ If a commit is substantial, add a blank line after the subject and a body paragraph explaining the why.
39
+
40
+ ## What never goes in commits
41
+
42
+ Never commit secrets, API keys, passwords, database credentials, or access tokens — not even to a non-production branch. If a secret is accidentally committed, treat it as compromised and rotate it immediately.
43
+
44
+ Never commit `.env` files. They are in `.gitignore` for a reason.
45
+
46
+ Never commit absolute local paths (e.g. `C:\WWW\2.0\worker2`). These are machine-specific and tracked only in developer Claude memory.
47
+
48
+ ## Knowledge base changes
49
+
50
+ Always run `node knowledge.js validate` before committing any changes to `knowledge/`. The pre-commit hook does this automatically in this repo. Do not bypass it with `--no-verify`.
51
+
52
+ If validation fails, fix the errors before committing. Do not commit a broken knowledge base.
53
+
54
+ ## Pull requests
55
+
56
+ PR title format: `[repo] brief description` — e.g. `[worker2] Add batch order processing`
57
+
58
+ PR description must include:
59
+ - Which repos or features are affected
60
+ - How to test the change
61
+ - Any migration steps (DB schema changes, config changes, queue registration)
62
+
63
+ Link to the relevant knowledge doc if one exists or was created as part of the PR.
64
+
65
+ ## Pre-commit checklist
66
+
67
+ Before creating a PR:
68
+ - [ ] `node knowledge.js validate` passes (if knowledge/ was touched)
69
+ - [ ] `php -l <changed files>` passes (no syntax errors)
70
+ - [ ] No commented-out code
71
+ - [ ] No hardcoded credentials or local paths
72
+ - [ ] Branch name follows naming convention
@@ -0,0 +1,118 @@
1
+ # Security Rules
2
+
3
+ These rules are critical. Violations create exploitable vulnerabilities. Follow them without exception.
4
+
5
+ ## SQL injection prevention
6
+
7
+ Never build SQL strings by concatenating or interpolating user input.
8
+
9
+ ```php
10
+ // CRITICAL VIOLATION — never do this
11
+ $sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
12
+ $sql = "SELECT * FROM orders WHERE email = '$email'";
13
+
14
+ // CORRECT — always use prepared statements
15
+ $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
16
+ $stmt->execute([$_GET['id']]);
17
+
18
+ // CORRECT — framework parameterized query
19
+ _Db::select('orders', ['email' => $email]);
20
+ ```
21
+
22
+ This applies to every SQL operation: SELECT, INSERT, UPDATE, DELETE, and DDL. No exceptions for "internal" data or "trusted" sources — use parameterized queries everywhere.
23
+
24
+ Integer casting (`(int) $_GET['id']`) reduces risk but is not a substitute for prepared statements. Use both.
25
+
26
+ ## XSS prevention
27
+
28
+ Never output user-supplied data to HTML without escaping.
29
+
30
+ ```php
31
+ // CRITICAL VIOLATION
32
+ echo $_GET['name'];
33
+ echo $user->bio; // if bio came from user input
34
+
35
+ // CORRECT
36
+ echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
37
+ echo htmlspecialchars($user->bio, ENT_QUOTES, 'UTF-8');
38
+ ```
39
+
40
+ `htmlspecialchars()` with `ENT_QUOTES` and `'UTF-8'` is the minimum. Use the framework's output escaping helper if one exists.
41
+
42
+ JSON output is safe from XSS but must use `json_encode()` — never manually build JSON strings.
43
+
44
+ JavaScript context requires additional escaping beyond `htmlspecialchars()`. Use `json_encode()` to pass server data to JS.
45
+
46
+ ## Password handling
47
+
48
+ Never store passwords in plaintext.
49
+
50
+ Never use MD5, SHA1, SHA256, or any fast hash for passwords. These are broken for password storage.
51
+
52
+ Always use `password_hash($password, PASSWORD_BCRYPT)` or `PASSWORD_ARGON2ID` to hash passwords.
53
+
54
+ Always use `password_verify($input, $hash)` to check passwords. Never compare hashes with `==` or `===`.
55
+
56
+ ## Sensitive data in logs
57
+
58
+ Never log passwords, session tokens, credit card numbers, bank account numbers, social security numbers, or any PII.
59
+
60
+ ```php
61
+ // VIOLATION
62
+ error_log('Login attempt: user=' . $email . ' password=' . $password);
63
+
64
+ // CORRECT
65
+ error_log('Login attempt: user=' . $email);
66
+ ```
67
+
68
+ Before adding any log statement, review what is being logged. When in doubt, log identifiers (user ID, order ID) not values.
69
+
70
+ ## Input validation
71
+
72
+ Never trust `$_GET`, `$_POST`, `$_COOKIE`, or `$_REQUEST` directly. Always validate at the boundary.
73
+
74
+ Validation means:
75
+ 1. **Type check**: Is this an integer, string, email, etc.?
76
+ 2. **Range/length check**: Is an integer within allowed range? Is a string within max length?
77
+ 3. **Format check**: Does a date match the expected format? Does an email pass `filter_var()`?
78
+ 4. **Allowlist check**: For values that must be one of a set, check against the set.
79
+
80
+ ```php
81
+ // VIOLATION — no validation
82
+ $page = $_GET['page'];
83
+
84
+ // CORRECT
85
+ $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, [
86
+ 'options' => ['min_range' => 1, 'max_range' => 1000],
87
+ 'flags' => FILTER_NULL_ON_FAILURE,
88
+ ]) ?? 1;
89
+ ```
90
+
91
+ ## File operations
92
+
93
+ Never use user input directly in file paths.
94
+
95
+ ```php
96
+ // CRITICAL VIOLATION
97
+ include($_GET['page'] . '.php');
98
+ file_get_contents('/var/uploads/' . $_GET['filename']);
99
+
100
+ // CORRECT — allowlist approach
101
+ $allowed = ['home', 'about', 'contact'];
102
+ $page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
103
+ include($page . '.php');
104
+ ```
105
+
106
+ ## Session security
107
+
108
+ Never put sensitive data in URLs (query strings are logged by web servers).
109
+
110
+ Regenerate session IDs after login: `session_regenerate_id(true)`.
111
+
112
+ Session cookies must be `HttpOnly` and `Secure` in production.
113
+
114
+ ## Cryptography
115
+
116
+ Never implement your own encryption. Use PHP's `sodium_*` functions or a vetted library.
117
+
118
+ Never use `rand()` or `mt_rand()` for security-sensitive random values. Use `random_bytes()` or `random_int()`.
@@ -0,0 +1,74 @@
1
+ # Testing Rules
2
+
3
+ ## Coverage requirements
4
+
5
+ Bug fixes require a regression test written before the fix is applied. The test must fail on the original code and pass after the fix.
6
+
7
+ New features require tests. A feature is not complete until its tests pass.
8
+
9
+ ## Test naming
10
+
11
+ Test names must describe the expected behavior, not just label the subject.
12
+
13
+ ```
14
+ // WRONG
15
+ test_get_user()
16
+ test_order_2()
17
+ test_process()
18
+
19
+ // CORRECT
20
+ test_returns_null_when_user_id_is_zero()
21
+ test_returns_404_when_order_not_found()
22
+ test_throws_when_required_param_missing()
23
+ ```
24
+
25
+ Use snake_case for test names. Start with `test_`. The name should read as a sentence describing what the system does under specific conditions.
26
+
27
+ ## What to test
28
+
29
+ Always test:
30
+ - The happy path (valid input, expected output)
31
+ - Empty input (empty string, empty array, zero)
32
+ - Null input where null is a possible value
33
+ - Boundary values (max int, max string length, empty collections)
34
+ - Invalid types where the function accepts loose input
35
+
36
+ For functions that interact with external systems (database, queue, HTTP):
37
+ - Test with the external call mocked or stubbed
38
+ - Test the error path: what happens when the external call fails?
39
+
40
+ ## Do not delete failing tests
41
+
42
+ If a test is failing, fix the root cause. Do not:
43
+ - Delete the test
44
+ - Comment out the assertion
45
+ - Change the test to match broken behavior
46
+ - Add a special case in production code to make the test pass without fixing the real bug
47
+
48
+ A failing test is information. Deleting it destroys information.
49
+
50
+ ## Test isolation
51
+
52
+ Each test must be independent. Tests must not depend on execution order. Tests must not share mutable state.
53
+
54
+ Reset any state modified by a test in a teardown method or by using transactions that are rolled back.
55
+
56
+ ## Framework-specific notes
57
+
58
+ For PHP unit tests (PHPUnit):
59
+ - Test class names end in `Test`: `OrderProcessorTest`
60
+ - Test classes extend `PHPUnit\Framework\TestCase`
61
+ - Use `setUp()` for per-test initialization, not class-level static state
62
+ - Use data providers for parameterized tests over copy-paste test methods
63
+
64
+ For integration tests that touch the database:
65
+ - Wrap each test in a transaction and roll back in `tearDown()`
66
+ - Never use the production database for tests
67
+
68
+ ## Regression tests
69
+
70
+ When fixing a bug, the regression test:
71
+ 1. Reproduces the exact scenario that caused the bug
72
+ 2. Is named to describe the bug: `test_does_not_crash_when_customer_is_null`
73
+ 3. Is placed in the same test file as the class under test
74
+ 4. Must be committed in the same PR as the fix
@@ -0,0 +1,104 @@
1
+ # Framework 1.0 (App_) Rules
2
+
3
+ These rules apply to all repos under `C:\WWW\1.0`: `library` (core) and `worker` (app).
4
+
5
+ ## Class naming
6
+
7
+ All classes must use the `App_` prefix. The prefix encodes the class type and path:
8
+
9
+ - Controllers: `App_Controller_<Name>` — e.g. `App_Controller_Orders`, `App_Controller_Dashboard`
10
+ - Models: `App_Model_<Name>` — e.g. `App_Model_User`, `App_Model_Order`
11
+ - Helpers/utilities: `App_Helper_<Name>` — e.g. `App_Helper_Date`, `App_Helper_Currency`
12
+ - Libraries/services: `App_Library_<Name>` or `App_Service_<Name>`
13
+
14
+ Never create a class in a 1.0 repo without the `App_` prefix. No exceptions for "small utility classes" or anonymous helpers.
15
+
16
+ ## Inheritance
17
+
18
+ Controllers must extend `App_Controller` (directly or through an intermediate base class that itself extends `App_Controller`).
19
+
20
+ ```php
21
+ // CORRECT
22
+ class App_Controller_Orders extends App_Controller { ... }
23
+ class App_Controller_Api_Orders extends App_Controller_Api { ... } // where App_Controller_Api extends App_Controller
24
+ ```
25
+
26
+ Models must extend `App_Model`.
27
+
28
+ ```php
29
+ // CORRECT
30
+ class App_Model_Order extends App_Model { ... }
31
+ ```
32
+
33
+ Never extend a raw PHP base class (like `stdClass` or nothing) for controllers or models in 1.0 repos.
34
+
35
+ ## Model instantiation
36
+
37
+ Never instantiate models directly in controllers using `new`.
38
+
39
+ ```php
40
+ // VIOLATION
41
+ $model = new App_Model_User();
42
+
43
+ // CORRECT — use the registry or factory
44
+ $model = App_Registry::get('User');
45
+ // or the framework's designated factory method
46
+ $model = $this->getModel('User');
47
+ ```
48
+
49
+ The registry ensures models are singletons within a request and allows the framework to manage dependencies. Direct instantiation bypasses this and causes subtle bugs with shared state.
50
+
51
+ ## Method naming
52
+
53
+ Public methods use `camelCase`.
54
+
55
+ ```php
56
+ // CORRECT
57
+ public function getOrdersByUser(int $userId): array { ... }
58
+ public function processPayment(array $params): bool { ... }
59
+
60
+ // VIOLATION — snake_case
61
+ public function get_orders_by_user($userId) { ... }
62
+ ```
63
+
64
+ Private and protected methods also use `camelCase`. No `snake_case` anywhere in 1.0 class method names.
65
+
66
+ ## File naming and location
67
+
68
+ Class file names match the class name segment after the last underscore, placed in the directory corresponding to the type:
69
+
70
+ - `App_Controller_Orders` → `controllers/Orders.php`
71
+ - `App_Model_User` → `models/User.php`
72
+ - `App_Helper_Date` → `helpers/Date.php`
73
+
74
+ Before adding a new class, check `knowledge/1.0/apps/<repo>/architecture.md` to confirm the correct directory structure for that repo.
75
+
76
+ ## Adding new classes
77
+
78
+ Before creating a new class in a 1.0 repo:
79
+
80
+ 1. Read `knowledge/1.0/apps/<repo>/architecture.md` — confirm the pattern used in that repo.
81
+ 2. Check if a similar class already exists — do not duplicate functionality.
82
+ 3. Follow the naming convention exactly, including the type segment.
83
+ 4. If you are adding a class to the `library` core repo, be aware it affects all 1.0 apps — review `knowledge/1.0/apps/library/architecture.md` first.
84
+
85
+ ## Error handling in controllers
86
+
87
+ Controllers must not let exceptions bubble up to the framework unhandled. Catch specific exceptions, log them, and return an appropriate response.
88
+
89
+ ```php
90
+ // CORRECT
91
+ public function processOrder(): void {
92
+ try {
93
+ $result = $this->getModel('Order')->process($this->request->post);
94
+ } catch (App_Exception_Validation $e) {
95
+ $this->response->error($e->getMessage(), 400);
96
+ return;
97
+ } catch (App_Exception_Database $e) {
98
+ error_log('Order processing DB error: ' . $e->getMessage());
99
+ $this->response->error('Internal error', 500);
100
+ return;
101
+ }
102
+ $this->response->success($result);
103
+ }
104
+ ```
@@ -0,0 +1,111 @@
1
+ # Framework 2.0 (_underscore) Rules
2
+
3
+ These rules apply to all repos under `C:\WWW\2.0`: `_underscore` (core), `worker2` (app), and `api2` (app).
4
+
5
+ ## Class naming
6
+
7
+ All framework classes use a leading underscore prefix:
8
+
9
+ - Controllers: `_Controller` base, subclasses `_Controller_<Name>`
10
+ - Models: `_Model` base, subclasses `_Model_<Name>`
11
+ - Workers: `_Worker` base, subclasses `_Worker_<Name>`
12
+ - API handlers: `_Api` base or `_Api_<Name>`
13
+
14
+ Application code in `worker2` and `api2` follows the same leading-underscore convention for framework-extending classes.
15
+
16
+ ## Worker contract
17
+
18
+ Every worker class must extend `_Worker` and implement the `run()` method.
19
+
20
+ ```php
21
+ // CORRECT
22
+ class _Worker_BatchOrders extends _Worker {
23
+ public function run(): void {
24
+ // process the job payload from $this->payload
25
+ }
26
+ }
27
+ ```
28
+
29
+ The `run()` method has no parameters and returns void. Job data is accessed via `$this->payload` (or the framework's equivalent property). Do not add parameters to `run()`.
30
+
31
+ ## Worker dispatch — never call directly
32
+
33
+ Workers must only be invoked via the queue dispatcher. Never call a worker's `run()` method directly from a controller, another worker, or any non-queue context.
34
+
35
+ ```php
36
+ // CRITICAL VIOLATION — direct call
37
+ $worker = new _Worker_BatchOrders($payload);
38
+ $worker->run();
39
+
40
+ // CORRECT — dispatch through queue
41
+ _Queue::dispatch('_Worker_BatchOrders', $payload);
42
+ // or the framework's equivalent dispatch method
43
+ ```
44
+
45
+ Direct calls bypass retry logic, error handling, visibility timeout, and monitoring. Even in development or testing, use the sync queue adapter — do not call `run()` directly.
46
+
47
+ ## API response envelope (api2 only)
48
+
49
+ Every action method in `api2` controllers must return an array with exactly these keys:
50
+
51
+ ```php
52
+ return [
53
+ 'success' => true, // bool — true on success, false on error
54
+ 'data' => $result, // mixed — the response payload (null on error)
55
+ 'errors' => [], // array — list of error messages (empty on success)
56
+ ];
57
+ ```
58
+
59
+ Never return a raw string, a bare array without these keys, or a nested different structure. The envelope is what clients depend on. Deviating from it breaks API consumers silently.
60
+
61
+ For error responses:
62
+
63
+ ```php
64
+ return [
65
+ 'success' => false,
66
+ 'data' => null,
67
+ 'errors' => ['Order not found'],
68
+ ];
69
+ ```
70
+
71
+ ## Checking dependencies before touching shared code
72
+
73
+ `dependsOn` in `knowledge/registry.json` means a repo extends or depends on another repo's classes. Before modifying a class in a dependency repo (e.g. `_underscore` core):
74
+
75
+ 1. Read `knowledge/2.0/apps/_underscore/architecture.md`.
76
+ 2. Check which repos depend on the class you are modifying (search `dependsOn` in registry.json).
77
+ 3. Verify your change does not break the contract expected by dependent repos.
78
+
79
+ Changing a `_Worker` base class method signature without checking `worker2` and `api2` is a common source of runtime failures.
80
+
81
+ ## Adding new worker action types
82
+
83
+ Before adding a new worker class in `worker2`:
84
+
85
+ 1. Read `knowledge/2.0/apps/worker2/architecture.md` — check the existing action type registry.
86
+ 2. Register the new worker in the appropriate config or registry file (the architecture doc identifies where).
87
+ 3. Add a corresponding feature doc: `knowledge/2.0/apps/worker2/features/<action-name>.md`.
88
+
89
+ Do not add a worker class without registering it. Unregistered workers cannot be dispatched.
90
+
91
+ ## Error handling in workers
92
+
93
+ Workers must catch specific exceptions and handle them appropriately. A worker that throws an uncaught exception will be retried by the queue (up to the configured retry limit) and then dead-lettered.
94
+
95
+ ```php
96
+ public function run(): void {
97
+ try {
98
+ $this->processOrder($this->payload['order_id']);
99
+ } catch (_Exception_OrderNotFound $e) {
100
+ // Do not retry — log and discard
101
+ error_log('Worker: order not found, discarding job: ' . $e->getMessage());
102
+ return;
103
+ } catch (_Exception_Database $e) {
104
+ // Retriable — re-throw so queue retries
105
+ error_log('Worker: DB error, will retry: ' . $e->getMessage());
106
+ throw $e;
107
+ }
108
+ }
109
+ ```
110
+
111
+ Distinguish between retriable errors (transient DB/network issues — re-throw) and non-retriable errors (bad data, missing record — log and return).