@xhulz/nina 0.44.0 → 0.46.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.
- package/package.json +1 -1
- package/releases/0.45.0/core/GAPS.md +125 -0
- package/releases/0.45.0/core/answered.json +41 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.45.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.45.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.45.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.45.0/core/tree/.claude/agents/devops.md +115 -0
- package/releases/0.45.0/core/tree/.claude/agents/implementer.md +158 -0
- package/releases/0.45.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.45.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.45.0/core/tree/.claude/agents/qa.md +152 -0
- package/releases/0.45.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.45.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.45.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.45.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.45.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.45.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.45.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.45.0/core/tree/.claude/patterns.md +248 -0
- package/releases/0.45.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.45.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.45.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.45.0/core/tree/.claude/router.md +198 -0
- package/releases/0.45.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.45.0/core/tree/CLAUDE.md +163 -0
- package/releases/0.45.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.45.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.45.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.45.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.45.0/core/vocabulary.json +13 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.45.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.45.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.45.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.45.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.45.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.45.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.45.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.45.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.45.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.45.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.45.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.45.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.45.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.45.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.45.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.45.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.45.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.45.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.45.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/releases/0.46.0/core/GAPS.md +125 -0
- package/releases/0.46.0/core/answered.json +41 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.46.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.46.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.46.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.46.0/core/tree/.claude/agents/devops.md +115 -0
- package/releases/0.46.0/core/tree/.claude/agents/implementer.md +158 -0
- package/releases/0.46.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.46.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.46.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.46.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.46.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.46.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.46.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.46.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.46.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.46.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.46.0/core/tree/.claude/patterns.md +248 -0
- package/releases/0.46.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.46.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.46.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.46.0/core/tree/.claude/router.md +198 -0
- package/releases/0.46.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.46.0/core/tree/CLAUDE.md +163 -0
- package/releases/0.46.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.46.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.46.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.46.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.46.0/core/vocabulary.json +13 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.46.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.46.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/dba.md +51 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.46.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.46.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.46.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.46.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.46.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.46.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.46.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.46.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.46.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.46.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.46.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.46.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.46.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.46.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.46.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.46.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.46.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/check.mjs +3 -3
- package/src/commands/compose.mjs +8 -4
- package/src/paths.mjs +15 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **No cache on a read that feeds a money write.** Anything that loads an amount or a balance before computing, approving or dispatching a transfer MUST NOT be cached. A stale amount contaminates a real money movement, and that damage is not undone by fixing the cache afterwards. **Hard reject if violated.**
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot money.2 -->
|
|
5
|
+
- **Monetary columns are integer.** Money is stored as an integer of the currency's minor unit ({{MINOR_UNIT}}); percentages are basis points. Reject `Decimal`, `Float`, or any type that can hold a fraction of the minor unit.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot money.3 -->
|
|
8
|
+
- **Money-integrity constraints.** When the change touches the record of an outbound transfer, verify the **unique constraint on its idempotency key** exists (`@unique` in the schema → `UNIQUE` in the migration SQL). That constraint is what still holds when the application-level guard fails; a missing or dropped one is a **hard reject**. Verify likewise any uniqueness the ownership model depends on. Report it in its own *Money-integrity* section, before the final verdict.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot money.4 -->
|
|
11
|
+
- Approve a cache policy on a query that feeds a money write.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot money.5 -->
|
|
14
|
+
- Approve a change to an outbound-transfer record that lacks the unique constraint on its idempotency key.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
|
|
3
|
+
## Money invariants you MUST honor
|
|
4
|
+
- **A distribution may NEVER exceed the amount** → block. Implement the reject; never clamp and continue.
|
|
5
|
+
- **A shortfall is parked, not distributed.** Surface it and leave the remainder where it is — never auto-distribute it, never absorb it quietly.
|
|
6
|
+
- **Transfers are idempotent — never sent twice.** Dedupe at the serialization point via the key, *before* the side effect. A retry, a re-delivered message or a double-click must not produce a second transfer.
|
|
7
|
+
- **Every state transition writes an audit row** (entity, from → to, actor). No transition without a trail.
|
|
8
|
+
- The deterministic money logic lives in `{{CORE_PKG}}` — call it as a pure function; never embed the arithmetic in a service.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot money.2 -->
|
|
11
|
+
- Embed money arithmetic (sums, deltas, value comparisons, distribution math) inline in a service. Computation lives in `{{CORE_PKG}}`.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **Confirm idempotency holds end to end.** Two sends with the same key → one logical transfer, no double send, and the serialization point refuses the second. This is the highest-blast-radius invariant in the system — drive it explicitly against the real service; do not infer it from reading the code.
|
|
3
|
+
- **Confirm money crosses the boundary as an integer of the minor unit**, and that conversion happens only in the module that owns it — never in a service, never through a floating-point type.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **Enforce the money invariants. Each violation is a HARD REJECT → loop back to architect:**
|
|
3
|
+
- **A distribution may NEVER exceed the amount.** It is blocked, not clamped. A retention is just another destination — same arithmetic, in integer {{MINOR_UNIT}}.
|
|
4
|
+
- **A shortfall must be parked, not silently distributed.** The remainder stays where it is, and the shortfall is surfaced to whoever is owed it.
|
|
5
|
+
- **Transfers must be idempotent — never sent twice.** Every transfer carries a key, and dedupe happens at the serialization point before the side effect. A retry, a re-delivered message or a double-click must never produce a second transfer.
|
|
6
|
+
- **Every state transition writes an audit row** (entity type, id, from, to, actor) and follows the transitions declared in `.claude/architecture.md`. An undeclared transition, or one missing its audit row, is a hard reject.
|
|
7
|
+
|
|
8
|
+
<!-- nina:slot money.2 -->
|
|
9
|
+
- **money-invariants** — a distribution never exceeds the amount; a shortfall is parked, never
|
|
10
|
+
distributed; transfers idempotent at the serialization point; an audit row on every transition;
|
|
11
|
+
integer {{MINOR_UNIT}} and basis points; no arithmetic outside `{{CORE_PKG}}`.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot money.3 -->
|
|
14
|
+
- `{{CORE_PKG}}` money logic touched (distribution and transfer generation are high-blast-radius)
|
|
15
|
+
|
|
16
|
+
<!-- nina:slot money.4 -->
|
|
17
|
+
- Approve a change that lets a distribution exceed the amount, silently distributes a shortfall, sends a transfer without idempotent dedupe, or skips an audit row — those are hard rejects, loop back to architect.
|
|
18
|
+
|
|
19
|
+
<!-- nina:slot money.5 -->
|
|
20
|
+
- **If APPROVED:** ≤300 words. Sections: Verification commands run (typecheck/lint/build only — NEVER vitest), Spec-match (✅/❌ per item), Hard-rule checks (tenant scoping ✅, integer {{MINOR_UNIT}} / bps ✅, each gate the diff triggered ✅ (`.claude/graph.md`), money invariants ✅ — distribution ≤ amount, shortfall parked, idempotent transfer, audit row on transition), QA-attention flags (list any condition from the "stricter audit" list so QA knows what to focus on), Final verdict.
|
|
21
|
+
|
|
22
|
+
<!-- nina:slot money.6 -->
|
|
23
|
+
- Money invariant violated (distribution exceeds the amount, shortfall silently distributed, non-idempotent transfer, missing audit row) → back to **architect** for redesign.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **Money-movement abuse (Hard Rules #3, #4, #5)** — **the money path reaches a real service; this is not a stub audit.** Is the idempotency contract actually enforced at the seam — the unique key, the serialization point, and the dedupe happening *before* the side effect? Can a replay, a double-click, a queue redelivery, or a manual reset of processed state cause a double send? Can a distribution exceed the amount, or a shortfall be silently distributed? Is whatever authorizes money OUT forgeable — a webhook without a verified signature, a callback trusted on its word? Is every state transition forced through an audit write? Flag any seam that is structurally unable to enforce its invariant, even where nothing has exploited it yet.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot money.2 -->
|
|
5
|
+
- **Idempotency-key & replay surface** — keys unguessable and collision-resistant where it matters; dedup enforced before the side effect, not after.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **The conservation property, asserted rather than argued.** Find the test that proves in equals
|
|
3
|
+
out plus retained, and check it holds on the adversarial paths too, not only the intended one.
|
|
4
|
+
Rounding that always favours the same party is a finding; so is a fee path with no upper bound.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
- **State the conservation property and assert it in the suite.** A contract that holds or routes an
|
|
3
|
+
amount has one — what goes in equals what goes out plus what is retained — and it belongs in a
|
|
4
|
+
test, not in a comment. Rounding is part of it: say which way it goes and who it favours.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Monetary values & percentages
|
|
6
|
+
|
|
7
|
+
- **Money is an integer of the currency's minor unit**, end to end — {{MINOR_UNIT}}, never a float, never a type that can hold a fraction of one. Source of precision: `{{MONEY_MODULE}}`.
|
|
8
|
+
- **Percentages are basis points** (`40% → 4000`, `100% → 10000`) so a distribution is exact without floating point.
|
|
9
|
+
- **Parsing, formatting and percentage arithmetic live in one module and nowhere else.** A second place that turns a string into money is a second rounding rule, and the two will disagree on a value somebody is owed.
|
|
10
|
+
- **Never** convert to a floating-point type before formatting, and never let a display format round a stored value.
|
|
11
|
+
- Monetary database columns are integer. JSON responses serialize them as **strings**, not numbers, so no client parses them into a float. The service owns the conversion.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot money.2 -->
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Distribution invariants (enforced by reviewer + unit tests)
|
|
18
|
+
|
|
19
|
+
The distribution engine lives in `{{CORE_PKG}}`. It is pure and total over integer {{MINOR_UNIT}}:
|
|
20
|
+
|
|
21
|
+
- A destination is either a percentage (basis points of the amount) or a fixed value.
|
|
22
|
+
- `total = Σ destinations`, computed in {{MINOR_UNIT}}.
|
|
23
|
+
- `total > amount` → **reject.** Never clamp, never distribute what is not there.
|
|
24
|
+
- `total < amount` → **allow**, surface the shortfall, and leave the remainder **parked**. Silence is the defect here: an unexplained remainder is indistinguishable from a bug.
|
|
25
|
+
- `total = amount` → OK.
|
|
26
|
+
- A retention is just another destination — the same arithmetic applies to it, with no special case.
|
|
27
|
+
|
|
28
|
+
<!-- nina:slot money.3 -->
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## State machines & idempotency (enforced by reviewer)
|
|
33
|
+
|
|
34
|
+
- **Every money-moving entity is a state machine.** Its states and legal transitions are declared in `.claude/architecture.md`; the code follows the declaration, not the other way round. Illegal transitions throw; they are never silently coerced.
|
|
35
|
+
- **Every transition writes an audit row** (entity type, entity id, from, to, actor). No transition without a trail — the trail is what makes a money bug diagnosable after the money has moved.
|
|
36
|
+
- **Outbound transfers are idempotent.** Each carries a unique key, and dedupe happens before the side effect, at the serialization point.
|
|
37
|
+
- **One serialization point per balance.** Whatever computes and dispatches against a single balance never runs concurrently with itself. Two concurrent runs over one balance is how a double send happens even when every individual write is correct.
|
|
38
|
+
|
|
39
|
+
<!-- nina:slot money.4 -->
|
|
40
|
+
- **Money computation and dispatch go through the serialization point.** Routes, queue consumers and tools never run a distribution or a transfer synchronously in the request path.
|
|
41
|
+
|
|
42
|
+
<!-- nina:slot money.5 -->
|
|
43
|
+
- **The distribution engine lives in `{{CORE_PKG}}`.** Services call it as a pure function; they do not embed money logic inline.
|
|
44
|
+
|
|
45
|
+
<!-- nina:slot money.6 -->
|
|
46
|
+
- Idempotency on money-moving writes: the record carries an idempotency key, and the serialization point refuses the second attempt *before* the side effect.
|
|
47
|
+
|
|
48
|
+
<!-- nina:slot money.7 -->
|
|
49
|
+
- `{{CORE_PKG}}` is the most-tested package — pure functions over fixtures. Cover the distribution invariants exhaustively: over, under, exact, mixed percentage and fixed, and rounding.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!-- nina:slot money.1 -->
|
|
2
|
+
3. **An outbound transfer is idempotent — it is NEVER sent twice.** Every transfer carries an idempotency key, and deduplication happens at a single serialization point **before** the side effect, never after it. A retry, a re-delivered queue message, a double-click and a replayed webhook must all collapse to one transfer. This is the highest-blast-radius invariant in the system: almost everything else can be corrected afterwards, and a duplicated transfer cannot.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot money.2 -->
|
|
5
|
+
4. **Conservation — what comes in equals what goes out plus what is retained.** A distribution may NEVER exceed the amount it distributes → **block**. If it is **less**, **alert** and leave the remainder **parked** and visible — never silently distributed, never silently absorbed. All arithmetic is integer {{MINOR_UNIT}}; percentages are basis points. The LLM never computes a monetary value.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot money.3 -->
|
|
8
|
+
5. **State-machine integrity.** Every money-moving entity has an explicit set of states and legal transitions, declared in `.claude/architecture.md` § *State machines* — not inferred from whatever code happens to exist. Only declared transitions are legal; an illegal one throws and is never silently coerced. **Every transition writes an audit row** (entity, id, from → to, actor, when). Skipping a state, inventing one, or landing a transition with no audit row is a hard reject.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot money.4 -->
|
|
11
|
+
- **money movement**: anything that creates, moves, settles or records a balance
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
- **Privacy.** No sensitive value in clear text where it must not be. The categories are declared in `.claude/architecture.md` § *Privacy* — they are not decided per query, and not decided by you. **Document bytes live only in the object store, referenced by key: the column holds the key, never the bytes.** Any column the architect's spec flagged for masking must be encrypted at rest. Reject a schema that widens what is stored in the clear.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
|
|
3
|
+
**Point it at an UNAUTHENTICATED route — a sign-in page is the right one.** Never sign in, and never
|
|
4
|
+
screenshot a signed-in page. Accounts on deployed environments hold real personal data, and a
|
|
5
|
+
screenshot or a console dump pulls it into agent transcripts and logs, where it persists and nobody
|
|
6
|
+
ever looks for it again. That is a privacy leak with no upside here: the sign-in page proves
|
|
7
|
+
everything this check is for. It renders from the same bundle, it exercises the same build-time env
|
|
8
|
+
inlining (a missing variable blanks it identically), and it needs no session.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
- Check for privacy regressions: **no sensitive value in a log, an error message, a URL or a test fixture**, per the categories in `.claude/architecture.md` § *Privacy*; document bytes only in the object store, never plaintext in the database; no leaked secret.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot pii.2 -->
|
|
5
|
+
- **privacy** — the privacy check above, in full: no sensitive value in a log, an error message, a
|
|
6
|
+
URL or a test fixture; document bytes only in the object store; no leaked secret. Where
|
|
7
|
+
**tenant-and-privacy** is dispatched, its reviewer owns this too and there is no separate one; where
|
|
8
|
+
it is not, this is a dimension of its own. Either way it is never nobody's.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
- **Privacy (Hard Rule #8)** — the categories declared in `.claude/architecture.md` § *Privacy* must never appear in logs, in responses that do not need them, in error paths, or in the database in clear text. Check every endpoint that returns a person's record for fields it includes by accident: a `select` that quietly grew a column is the usual way this leaks. Check the error paths too — they are written last and reviewed least.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
- **Nothing personal reaches the chain.** What is written on-chain is public, permanent, and beyond
|
|
3
|
+
deletion by anyone — including by you, and including after a subject asks. A hash of a personal
|
|
4
|
+
identifier is not a protection: it is a lookup table for whoever holds the input set. Reject a
|
|
5
|
+
personal datum stored in state, emitted in an event, or passed as calldata.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot pii.1 -->
|
|
2
|
+
8. **Sensitive personal data is classified, and the classification is enforced.** What counts as sensitive here — identity documents, financial identifiers, anything a person would not expect to be readable by whoever can read a log — is declared in `.claude/architecture.md` § *Privacy*, along with the regime that governs it ({{PRIVACY_REGIME}}). Encrypted at rest. **No sensitive value in a log, an error message, a URL, an analytics event, or an agent transcript** — the places nobody thinks about are exactly the ones that persist. Document bytes live only in the object store, referenced by key, never in the database in clear text.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot pii.2 -->
|
|
5
|
+
- **personal data**: where it is collected, stored, logged or sent
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
| `prisma-cli` | **before ANY `prisma` CLI command** — `validate`, `format`, `migrate dev`, `migrate diff`, `migrate deploy`. Flags differ between versions, and one of them (`migrate diff --shadow-database-url` against a live database) destroys the database it points at |
|
|
3
|
+
| `prisma-client-api` | auditing a new or changed query — filters, operators, `$transaction` semantics. Through a driver adapter, cite the adapter's installed source for what a transaction does: the skill describes the default engine, and an adapter can roll back less |
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot prisma.2 -->
|
|
6
|
+
Neither skill covers a **caching or edge layer sitting on top of the client** — cache strategy,
|
|
7
|
+
edge-client behaviour and the rule about never caching a read that authorizes a write belong to that
|
|
8
|
+
layer's own integration doc, declared like any other integration. A skill does not replace it.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot prisma.3 -->
|
|
11
|
+
- **With Prisma, both offline.** Run `pnpm --filter {{DB_PKG_NAME}} exec prisma validate`; it must pass. Then run `prisma migrate diff` in its **offline, file-only form** and read the generated SQL, not just the Prisma delta. Its flags changed in Prisma 7, so read the installed major (`prisma --version`) before writing the command:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
# Prisma 6. Compare the PREVIOUS schema against the working one — pure file-to-file, no database.
|
|
15
|
+
git show HEAD:{{DB_PKG}}/prisma/schema.prisma > /tmp/schema-head.prisma
|
|
16
|
+
pnpm --filter {{DB_PKG_NAME}} exec prisma migrate diff \
|
|
17
|
+
--from-schema-datamodel /tmp/schema-head.prisma \
|
|
18
|
+
--to-schema-datamodel prisma/schema.prisma \
|
|
19
|
+
--script
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
From Prisma 7 the two flags are `--from-schema` and `--to-schema`, and the schema engine starts from the
|
|
23
|
+
datasource in `prisma.config.ts` even for this diff: with none there, it prints nothing and exits 0. For
|
|
24
|
+
SQLite a placeholder the diff never opens is enough, a `file:` URL under a gitignored directory; for any
|
|
25
|
+
other provider, find out what the diff opens before you give it one. Never a real database's URL. A first schema has nothing at `HEAD`: diff it `--from-empty`. Compare the output against the
|
|
26
|
+
hand-written `migration.sql` to confirm the declarative parts match.
|
|
27
|
+
|
|
28
|
+
⚠️ **Do NOT reach for `--from-migrations`.** It looks like the natural choice and it is a trap:
|
|
29
|
+
Prisma refuses it without a shadow database (`--shadow-database-url`, or from Prisma 7 a
|
|
30
|
+
`shadowDatabaseUrl` in `prisma.config.ts`), which pushes you toward supplying a database URL — the exact move that destroys databases (below). The file-to-file form above answers the
|
|
31
|
+
same question with no database involved.
|
|
32
|
+
|
|
33
|
+
⛔ **NEVER point `--shadow-database-url`, `--from-url`, `--to-url`, `--from-schema-datasource` — or, from Prisma 7, a `shadowDatabaseUrl` or the config datasource behind `--from-config-datasource` — at a real database, most of all not the one `{{SECRETS_LOCAL}}` names.** Prisma **executes** the entire migration history against whatever database those flags name, which **DESTROYS ALL DATA** in it. A shadow database must be a disposable, empty database and nothing else. It does not announce itself: the command completes with a reassuring "empty migration" result and no error, having already destroyed everything in the database it was pointed at. If the offline form above cannot answer your question, **STOP and report that to the orchestrator** instead of reaching for a database URL.
|
|
34
|
+
|
|
35
|
+
<!-- nina:slot prisma.4 -->
|
|
36
|
+
- **Where reads are cached through Prisma Accelerate,** every `findUnique` / `findMany` on a hot read path names its `cacheStrategy` (`ttl` / `swr`), and one that feeds a correctness-critical write names none.
|
|
37
|
+
|
|
38
|
+
<!-- nina:slot prisma.5 -->
|
|
39
|
+
- **On an edge runtime,** new Prisma code paths use the edge client of the installed major: `@prisma/client/edge` up to Prisma 6; from 7, the client the `prisma-client` generator writes with the platform's `runtime` set, imported from where it is generated — `@prisma/client/edge` fails there with `MODULE_NOT_FOUND`.
|
|
40
|
+
|
|
41
|
+
<!-- nina:slot prisma.6 -->
|
|
42
|
+
- Run a Prisma command that writes to a real database — `prisma migrate dev`, `migrate deploy`, `migrate reset`, `migrate resolve`, `db push`, `db execute`, `$executeRaw*` — or **any `prisma migrate diff` variant carrying a database URL**, which silently wipes the database it points at.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
- **`prisma-cli`** — before `migrate deploy` or any other Prisma CLI command against a real database.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot prisma.2 -->
|
|
5
|
+
- **Prisma migrations go out with `prisma migrate deploy`**, and nothing else changes a shared database's schema.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot prisma.3 -->
|
|
8
|
+
- Run `prisma migrate diff --shadow-database-url <url>` against any live database. It **executes** the migration history against that database, destroying everything in it, and reports success. Use a disposable shadow database or don't run it.
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot prisma.4 -->
|
|
11
|
+
- Run `prisma migrate reset` against a shared database, ever.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
- **Database table/column names** (Prisma `@map` / `@@map`): `snake_case` in SQL, `camelCase` in TS.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot prisma.2 -->
|
|
5
|
+
- Where Prisma reads are cached through Accelerate, `cacheStrategy` lives in `{{DB_PKG}}`: services do not add, strip, or override cache policies.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot prisma.3 -->
|
|
8
|
+
|
|
9
|
+
### Prisma
|
|
10
|
+
|
|
11
|
+
- Schema lives in `{{DB_PKG}}/prisma/schema.prisma`. Nowhere else.
|
|
12
|
+
- **Never write raw SQL** unless justified in a code comment with perf data.
|
|
13
|
+
- N+1 is solved with `include` / `select`, chosen deliberately.
|
|
14
|
+
- Where reads are cached through Accelerate, every `findUnique` / `findMany` on a hot path defines its `cacheStrategy`, and no direct connection string is embedded in code.
|
|
15
|
+
- Tests run a real Prisma client against a real database; the client is never mocked.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
| Run any `prisma` CLI command (the database gate, devops, and the integration gate where there is one) | `prisma-cli` | flags differ between versions; `migrate diff --shadow-database-url` against a live DB wiped dev/staging once |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot prisma.2 -->
|
|
5
|
+
| Write or audit a Prisma query (architect, implementer, the database gate, reviewer) | `prisma-client-api` | filters, operators, `$transaction` semantics. a caching or edge layer on top of the client is **NOT covered** — its cache strategy belongs to that layer's own integration doc |
|
package/src/commands/check.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readFile, readdir } from 'node:fs/promises';
|
|
12
|
-
import { describeMove, HARNESS, interruptedMove, readProfile, surfacesIn } from '../paths.mjs';
|
|
12
|
+
import { deferredWhy, describeMove, HARNESS, interruptedMove, readProfile, surfacesIn } from '../paths.mjs';
|
|
13
13
|
import { expectedUnfilled } from '../expected.mjs';
|
|
14
14
|
import { parseGraph, validateGraph } from '../graph.mjs';
|
|
15
15
|
import { frontmatterFindings, installedSkills, modelFindings, toolFindings } from '../tools.mjs';
|
|
@@ -243,7 +243,7 @@ export async function check(argv, ctx) {
|
|
|
243
243
|
const deferredUsed = new Set();
|
|
244
244
|
let deferrable = false;
|
|
245
245
|
const owe = (key, problem) => {
|
|
246
|
-
const why =
|
|
246
|
+
const why = deferredWhy(profile, key);
|
|
247
247
|
if (why) {
|
|
248
248
|
deferredUsed.add(key);
|
|
249
249
|
notes.push(`deferred: ${key} — ${why}`);
|
|
@@ -298,7 +298,7 @@ export async function check(argv, ctx) {
|
|
|
298
298
|
// one only to tailor it.
|
|
299
299
|
const defaulted = await defaultedSlots(resolved.dir);
|
|
300
300
|
const open = [...slots].filter((s) => !filled.has(s) && !defaulted.has(s));
|
|
301
|
-
const missing = open.filter((s) => !expected.has(s) && !(
|
|
301
|
+
const missing = open.filter((s) => !expected.has(s) && !deferredWhy(profile, s));
|
|
302
302
|
for (const s of open.filter((s) => !expected.has(s) && !missing.includes(s))) owe(s, `project slot ${s} has no fragment`);
|
|
303
303
|
const awaited = open.filter((s) => expected.has(s));
|
|
304
304
|
const orphan = [...filled].filter((s) => !slots.has(s));
|
package/src/commands/compose.mjs
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
20
|
-
import { HARNESS, layerRootFor, readProfile } from '../paths.mjs';
|
|
20
|
+
import { HARNESS, deferredWhy, layerRootFor, readProfile } from '../paths.mjs';
|
|
21
21
|
import { expectedUnfilled } from '../expected.mjs';
|
|
22
22
|
import { existsSync, lstatSync, readFileSync } from 'node:fs';
|
|
23
23
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
@@ -553,8 +553,8 @@ export async function composeProject(target, ctx, options = {}) {
|
|
|
553
553
|
}
|
|
554
554
|
// A name the project deferred, with its reason, reads as not decided: a stage that meets it asks
|
|
555
555
|
// instead of guessing, and the raw placeholder it would otherwise read looks like an editing slip.
|
|
556
|
-
for (const
|
|
557
|
-
if (
|
|
556
|
+
for (const name of Object.keys(profile.deferred ?? {})) {
|
|
557
|
+
if (deferredWhy(profile, name) && NAME.test(name)) text = text.split(`{{${name}}}`).join(`[${name}: not decided yet]`);
|
|
558
558
|
}
|
|
559
559
|
// What is still a placeholder is owed: no default answers it, and the project has not declared it, or has
|
|
560
560
|
// declared it null. Left raw, it reached a stage as `{{TENANT_KEY}}` — once inside a shell block it was told to
|
|
@@ -611,8 +611,12 @@ export async function compose(argv, ctx) {
|
|
|
611
611
|
// A surface's own slot left empty is a bug in the harness, not the project's to fill, so drift mode
|
|
612
612
|
// still reports it; only the project's slots are left to the declaration detector.
|
|
613
613
|
const reported = driftOnly ? result.unfilled.filter((slot) => !/ project\.\d+$/.test(slot)) : result.unfilled;
|
|
614
|
-
|
|
614
|
+
// A project slot the profile defers, with its reason, is a decision not made yet, here as in `check`.
|
|
615
|
+
const { profile } = readProfile(target);
|
|
616
|
+
const deferred = reported.filter((slot) => !expected.has(slot) && deferredWhy(profile, slot));
|
|
617
|
+
const owed = reported.filter((slot) => !expected.has(slot) && !deferred.includes(slot));
|
|
615
618
|
const awaited = reported.filter((slot) => expected.has(slot));
|
|
619
|
+
if (deferred.length > 0) console.log(` ${deferred.length} slot(s) deferred in ${HARNESS}/profile.json — \`nina check\` says why each waits`);
|
|
616
620
|
if (owed.length > 0) {
|
|
617
621
|
console.log(` ${owed.length} slot(s) have no fragment in a declared layer:`);
|
|
618
622
|
for (const slot of owed.slice(0, 12)) console.log(` ${slot}`);
|
package/src/paths.mjs
CHANGED
|
@@ -125,6 +125,21 @@ export function readProfile(target) {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Why a profile defers a name — a vocabulary name, a document, or a project slot as `<path> <slot id>` — or
|
|
130
|
+
* `''` when it does not. A deferral with no reason counts for nothing: `check` says so, and every other reader
|
|
131
|
+
* treats the name as owed. `compose --check` once read the profile's deferrals not at all, and failed on six
|
|
132
|
+
* slots a new project had deferred while `check` was clean.
|
|
133
|
+
*
|
|
134
|
+
* @param {object | undefined} profile - The parsed profile.
|
|
135
|
+
* @param {string} key - The deferred name.
|
|
136
|
+
* @returns {string} The reason, trimmed; empty when the name is not deferred.
|
|
137
|
+
*/
|
|
138
|
+
export function deferredWhy(profile, key) {
|
|
139
|
+
const why = profile?.deferred?.[key];
|
|
140
|
+
return typeof why === 'string' ? why.trim() : '';
|
|
141
|
+
}
|
|
142
|
+
|
|
128
143
|
/**
|
|
129
144
|
* Where a project's layers are read from.
|
|
130
145
|
*
|